API Authentication

All DanubeData API requests require authentication using Laravel Sanctum bearer tokens.

Creating an API Token

Step 1: Open Security, API tokens

  1. Log in to your DanubeData account
  2. Open Security from the account menu in the top right (the sidebar calls it Credentials)
  3. Open the API tokens tab

Step 2: Create a New Token

  1. Click Create token
  2. Enter a descriptive name for the token (e.g., "Production Server", "CI/CD Pipeline")
  3. Choose its Project access: This project only (default) or All your projects (see Project Scope)
  4. Tick the permissions it needs. They are grouped by resource; Defaults, Select all and Clear set them all at once
  5. Click Create token, then confirm it's you when asked

Step 3: Save Your Token

⚠️ Important: The token will only be shown once. Copy it immediately and store it securely.

Text
Token: 7TEwyZaQXMXRVBZV9USjWNRbXAbPv9BrgMJSLDCk345196d2

Using Your Token

In HTTP Headers

Include the token in the Authorization header with the Bearer prefix:

Bash
curl -H "Authorization: Bearer YOUR_TOKEN_HERE" \
     https://danubedata.ro/api/v1/vps

The scheme name is case-insensitive (bearer works too), and a token works only with the API, never with console pages, which refuse it with a 401.

Example with cURL

Bash
curl -X GET \
  'https://danubedata.ro/api/v1/vps' \
  -H 'Authorization: Bearer 1|7TEwyZaQXMXRVBZV9USjWNRbXAbPv9BrgMJSLDCk345196d2' \
  -H 'Accept: application/json'

Example with JavaScript/Axios

JavaScript
const axios = require('axios');

const api = axios.create({
  baseURL: 'https://danubedata.ro/api/v1',
  headers: {
    'Authorization': 'Bearer YOUR_TOKEN_HERE',
    'Accept': 'application/json'
  }
});

// Make a request
api.get('/vps')
  .then(response => console.log(response.data))
  .catch(error => console.error(error));

Example with Python

Python
import requests

headers = {
    'Authorization': 'Bearer YOUR_TOKEN_HERE',
    'Accept': 'application/json'
}

response = requests.get(
    'https://danubedata.ro/api/v1/vps',
    headers=headers
)

print(response.json())

Example with PHP

PHP
<?php

$token = 'YOUR_TOKEN_HERE';
$url = 'https://danubedata.ro/api/v1/vps';

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $token,
    'Accept: application/json'
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
$data = json_decode($response, true);

curl_close($ch);
print_r($data);

Token Permissions

Tokens can have different permission scopes. Each is {resource}:{action}, and you choose them when you create the token.

Available Scopes

ResourceReadWriteDeleteOther
VPS instances and block volumesvps:readvps:writevps:deletevps:diagnostics, vps:credentials
Databasesdatabase:readdatabase:writedatabase:deletedatabase:diagnostics, database:credentials
Cachescache:readcache:writecache:deletecache:diagnostics, cache:credentials
Queuesqueue:readqueue:writequeue:deletequeue:diagnostics, queue:credentials
Parameter groupsparameter-group:readparameter-group:writeparameter-group:delete
Firewallsfirewall:readfirewall:writefirewall:delete
Snapshotssnapshot:readsnapshot:writesnapshot:delete
SSH keysssh-key:readssh-key:writessh-key:delete
Webhookswebhook:readwebhook:write-
Secrets API (/api/secrets)secret:readsecret:write-
Object storagestorage:readstorage:writestorage:deletestorage:diagnostics
Managed appsapp:readapp:writeapp:deleteapp:diagnostics, app:credentials
Uptime checksuptime:readuptime:writeuptime:delete
Resource alertsresource-alert:readresource-alert:writeresource-alert:delete
Metric alerts (the older name of resource alerts, still accepted)metric-alert:readmetric-alert:writemetric-alert:delete
Container registryregistry:readregistry:writeregistry:delete
Serverless containers (Rapids)serverless:readserverless:writeserverless:deleteserverless:diagnostics
Static sitesstatic-site:readstatic-site:writestatic-site:deletestatic-site:diagnostics
Managed Kuberneteskubernetes:readkubernetes:writekubernetes:delete
Audit logaudit:read--audit:export, audit:raw
Support ticketssupport:readsupport:write-
Account limits---account-limits:request
  • Read views and lists. Write creates, changes and acts. Delete deletes.
  • {resource}:diagnostics is separate from :read on purpose: a token that may list your databases cannot read what they print to their logs unless you also grant it.
  • app:credentials returns a managed app's working admin password, so it is separate from app:read. The same holds for vps:credentials, database:credentials, cache:credentials and queue:credentials: see Reading passwords.
  • secret:read reads the values stored through the Secrets API and secret:write creates, changes and deletes them.
  • registry:write is reserved: repositories are created by pushing an image, so nothing writes through the API yet.
  • account-limits:request lets a token ask for higher account limits and read the status of that request. It changes nothing by itself: the request waits for the project owner to approve it in the console. See Requesting with an API token.
  • support:read and support:write open the Support Tickets API. A ticket opened or a reply sent with a token is marked as such, and support verifies the sender before changing anything or sharing account data.
  • New tokens start with read access to VPS, databases, caches, storage and static sites: vps:read, database:read, cache:read, storage:read and static-site:read. Nothing else is on by default.
  • A token keeps the abilities it was created with. One you made before an ability existed does not get it: open the token on Security, API tokens, tick it and save, or create a new token. The exception is a token with all abilities, such as the one danube login creates: it holds every ability, ones added later included.

Reading passwords

read never includes a password. The routes that return a working password need a separate credentials permission, so a token that only lists or monitors a resource cannot come away with the keys to it.

PermissionWhat it unlocks
vps:credentialsGET /api/v1/vps/{id}/password, the root password
database:credentialsGET /api/v1/database/{id}/credentials, the superuser and application passwords
cache:credentialsGET /api/v1/cache/{id}/credentials, the cache password
queue:credentialsThe password in GET /api/v1/queue/{id}/connection-info
app:credentialsGET /api/v1/apps/{id}/credentials, the admin password

Without the permission, a route that exists to return a password answers 403.

Each time a token is handed a password, your project's Activity Log gets an entry that names the token and the resource, so a token that should not be reading passwords shows up there. A token that reads the same password again within ten minutes is one entry, and the password itself is never written to the log. Reading a password from the console is not an API token read and is not logged this way.

The routes that describe how to connect stay open to read: GET /api/v1/cache/{id}/connection-info, GET /api/v1/queue/{id}/connection-info, and GET on the database, cache and queue instance itself. Without the credentials permission they leave the secret out. password, and any connection URL (connection_info, amqp_url, which contains the password), come back as null. The host, port, username and vhost are still there.

Only project owners and administrators can give a token a password permission. The token form does not list them for an editor, and a token an editor makes or edits never gets one. A token that can act in all of your projects gets a password permission only if your role holds it in every one of them, so an editor in any of your projects means an all-projects token without them. The role is checked when a token is made or edited, not when it is used, so a token that already holds a password permission keeps it, and you can take it away.

Starting a database password rotation needs database:write. Reading the new password, or polling credential_rotation, is GET /api/v1/database/{id}/credentials, so it needs database:credentials as well.

The Secrets API returns the values you stored in it, so it has the same split: secret:read reads them (add ?include_value=true to get the values) and secret:write creates, changes and deletes them. Neither is on by default and neither implies the other. The Secrets API lists what you stored: the passwords DanubeData generated for your databases, caches and apps are not in it, and are read from each resource's credentials route with the permission above.

None of these permissions is on by default. Select them when you create a token that has to read passwords. Tokens that already existed and had not expired were given what they could use before: the matching credentials permission for each resource they could read, and secret:read and secret:write. Automation that already read passwords or secrets keeps working. If a token does not need to, open it on Security, API tokens and untick the permission.

Principle of Least Privilege

Always create tokens with the minimum permissions required for their purpose:

  • Read-only tokens for monitoring and reporting
  • Write tokens only for automation that needs to create/modify resources
  • Separate tokens for different services/environments
  • support:write only where it is needed, because what a token sends is not something we can treat as coming from a person you know

Project Scope

In addition to permission scopes, every token is bound to a project scope that controls which of your projects it can touch. Choose the scope when you create the token:

  • This project only (default for new tokens) — the token is locked to the project you created it in and can never act on any other project. Ideal for a single integration or environment.
  • All your projects — an account-wide token that can act on any project you belong to.

The Project column of the token list shows the project a token is locked to, or All your projects. A token locked to a project you have since left is refused, and the list says A project you have left.

Selecting a project with an account-wide token

An account-wide token acts on your current project by default: the one you last switched to in the console. To target a specific project, send its ID in the X-Team-Id header:

Bash
curl -H "Authorization: Bearer YOUR_TOKEN" \
     -H "X-Team-Id: 42" \
     https://danubedata.ro/api/v1/vps

Project-locked tokens

A project-locked token ignores any project other than the one it is bound to. If you send an X-Team-Id for a different project, the request is refused:

JSON
{
  "message": "This token is scoped to a single project and cannot access the requested team.",
  "error": "Forbidden"
}

Existing tokens keep working. Tokens created before project scoping remain account-wide (All your projects) — nothing changes unless you choose to tighten them by creating a new project-locked token.

Managing Tokens

Viewing Active Tokens

The API tokens tab on Security lists your tokens with:

  • Token name
  • The project it reaches
  • A summary of its permissions
  • Last used date

Open a token to see when it was created and when it expires, and to change its permissions. A token made by danube login holds every permission, including any added later; saving a change to it keeps only what is ticked.

Revoking Tokens

To revoke a token:

  1. Open it on the API tokens tab
  2. Click Delete
  3. Confirm the deletion

⚠️ Warning: Revoking a token immediately invalidates it. Any services using that token will stop working.

Security Best Practices

Storage

  • Never commit tokens to version control
  • Store tokens in environment variables or secure vaults
  • Use different tokens for different environments (dev, staging, prod)

Rotation

  • Rotate tokens regularly (e.g., every 90 days)
  • Immediately revoke tokens when:
    • An employee leaves
    • A service is decommissioned
    • A token may have been compromised

Monitoring

  • Check token usage regularly on the API tokens tab
  • Investigate any unexpected "Last used" dates
  • Set up alerts for unusual API activity

Authentication Errors

401 Unauthorized

Error Response:

JSON
{
  "message": "Unauthenticated."
}

Causes:

  • Missing Authorization header
  • Invalid token format
  • Revoked or expired token

Solution:

  • Verify the token is included correctly
  • Check the token hasn't been revoked
  • Create a new token if needed

403 Forbidden

Error Response:

JSON
{
  "message": "This action is unauthorized."
}

Causes:

  • Token lacks required permissions
  • Trying to access resources from another team
  • Using a project-locked token against a different project (see Project Scope)

Solution:

  • Verify the token has the necessary scopes
  • Create a new token with appropriate permissions
  • Ensure you're accessing resources from your team
  • If the token is project-locked, use it only against its own project, or create an All your projects token

Testing Authentication

In the Interactive Docs

  1. Visit /docs/api in your browser
  2. Click the Authorize button (top right)
  3. Enter: Bearer YOUR_TOKEN_HERE
  4. Click Authorize
  5. Try any endpoint to verify it works

Using cURL

Bash
# Test with a simple endpoint
curl -H "Authorization: Bearer YOUR_TOKEN" \
     https://danubedata.ro/api/v1/vps

# Should return your VPS instances or an empty array

Next Steps

  • Visit /docs/api to explore all authenticated endpoints
  • Learn about rate limits
  • Set up webhooks for event notifications