Authentication
Two credentials let you call the Manager yourself: a Personal Access Token (a token tied to your user account) and a machine user (a service account for a backend). Each works on a different set of routes, listed below. Browser users need neither; the proxy logs them in with OAuth and forwards their identity.
Authentication Methods
Personal Access Tokens (PAT)
For user-driven API access: CLI tools, scripts, third-party integrations, development and testing.
It works on the GraphQL API (/query) and the pilet management routes (/pilets/*), reached through the proxy (see the warning below). Only a PBKDF2-SHA256 hash (600,000 iterations) is stored; the token itself is shown once.
Machine Users
For service-to-service authentication: backend services, automated workflows, trigger execution and system integrations.
It works on /api/machine/* and /esb/* on the Manager, and on application routes behind the proxy. Send it as a Bearer token on the Manager routes; Basic auth is accepted only on proxy-fronted application routes (see below).
Personal Access Tokens
Creating a PAT
- Navigate to Personal Access Tokens in sidebar
- Click Create Token
- Enter Token Name (e.g., "CLI Access")
- Optional: Set Expiration Date
- Click Create
- Copy the token - shown only once!
Using a PAT
Send it as a Bearer token. Tokens look like pat_<8 chars>_<32 chars>.
curl -H "Authorization: Bearer pat_abc123..." \
https://manager.example.com/queryCall /query through the proxy host, not the Manager port
/query and /pilets/* first check the proxy shared secret (X-Proxy-Auth) and only then read your PAT. A request sent straight to the Manager's port is rejected with 401 and a JSON body explaining that the request did not come through the trusted proxy. Use the proxy-fronted hostname; the proxy validates the PAT and adds the required headers.
With a GraphQL client (here urql):
import { createClient } from "urql";
const client = createClient({
url: "https://manager.example.com/query",
fetchOptions: {
headers: {
authorization: `Bearer ${personalAccessToken}`,
},
},
});Managing PATs
The token list shows each token's name, prefix, last-used time and expiry. To revoke one, find it in the list, click Delete and confirm; the token is invalidated immediately.
Recommendations:
- Name tokens descriptively
- Set expiration dates
- Rotate tokens regularly
- Revoke unused tokens
- Never commit tokens to git
Machine Users
Creating a Machine User
- Navigate to Machine Users
- Click Create Machine User
- Enter Name
- Select Authentication Type:
- Bearer Token - Token-based (recommended)
- Basic Auth - Username/password
- Click Create
- Copy credentials - shown only once!
Bearer Token Authentication
Recommended. The token is a random 32-byte value; the Manager stores only its hash.
curl -H "Authorization: Bearer <machine-user-token>" \
https://manager.example.com/api/machine/checkconst response = await fetch("/api/machine/register-backend", {
method: "POST",
headers: {
Authorization: `Bearer ${machineUserToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
callback_url: "https://backend.example.com/triggers",
application_id: "app-uuid",
}),
});Basic Authentication
Basic auth does not work on /api/machine/* or /esb/*
MachineUserAuthMiddleware accepts Bearer tokens only. A Basic-auth request to any of those routes is rejected before the credential is looked up:
{ "error": "Invalid authorization format. Expected: Bearer <token>" }Basic-auth machine users exist, but they are validated by the proxy, which forwards the header to the Manager's POST /api/validate-proxy-auth endpoint. Use them for applications sitting behind the proxy; use a Bearer token for anything talking to the Manager's own machine endpoints.
Behind the proxy, a Basic-auth machine user is used exactly like any other credential on an application route:
curl -u "username:password" \
https://myapp.example.com/some/application/routeMachine User Permissions
Machine users are scoped to:
- A single environment. A machine user belongs to one environment, not to one tenant. One machine user serves every tenant its application is enabled for; the tenant travels in the request body (for comms, the tenant list, and so on) and the Manager checks it against the credential, so a caller can never reach a tenant its credential is not entitled to.
- Applications in that environment's project. Registration and comms calls name an application, and the Manager rejects one that belongs to another project.
- Specific ESB endpoints. ESB access is granted per endpoint on the machine user's access tab.
API Endpoints
Check Authentication (Machine User)
Verify machine user credentials:
GET /api/machine/check
Authorization: Bearer <token>Response (always 200; authenticated is false with a message when the credential did not resolve):
{
"authenticated": true,
"machine_user_id": "uuid",
"environment_id": "uuid"
}Register Backend
Register for trigger execution:
POST /api/machine/register-backend
Authorization: Bearer <token>
Content-Type: application/json
{
"callback_url": "https://backend.example.com/triggers",
"version": "1.0.0",
"application_id": "uuid"
}Response (201 for a new registration, 200 when an existing registration for the same application and machine user was updated):
{
"success": true,
"registered_id": "uuid",
"token": "callback-auth-token",
"message": "Backend registered successfully",
"environment_id": "uuid",
"application_id": "uuid"
}The Manager sends the returned token as Authorization: Bearer <token> on every trigger callback to your callback URL. Registering again issues a new token and invalidates the old one, so always keep the token from the latest response.
Security Best Practices
Token Storage
Store tokens in environment variables or a secrets manager (HashiCorp Vault, AWS Secrets Manager), encrypted at rest. Do not commit them to version control, hardcode them in source, or share them over unsecured channels.
Token Rotation
- Create new token
- Update applications to use new token
- Verify new token works
- Revoke old token
Access Control
- Use least privilege principle
- Create separate tokens per service
- Revoke tokens when no longer needed
- Monitor token usage in audit logs
Network Security
- Use HTTPS only
- Restrict by IP if possible
- Implement rate limiting
- Monitor for unusual patterns
Troubleshooting
401 Unauthorized
Reasons: an invalid, expired or revoked token; the wrong header format (Basic auth on a /api/machine/* or /esb/* route); a PAT sent straight to the Manager port instead of through the proxy.
To fix:
- Read the JSON
errorfield in the response; it names the reason - Verify token is correct and has not expired
- Ensure header format:
Authorization: Bearer <token> - Create new token if needed
429 Too Many Requests
Repeated failed authentications against /api/machine/* or /esb/* from one client trip a throttle. Wait for the number of seconds in the Retry-After header before trying again.
403 Forbidden
Reasons: valid authentication but insufficient permissions; a resource in another tenant; a machine user whose environment's project does not contain the application.
To fix:
- Verify tenant/application access
- Check user permissions
- Ensure correct resource IDs
Token Not Working After Creation
Check:
- Did you copy the entire token?
- Are there extra spaces or newlines?
- Is the header formatted correctly?
- Try the
/api/machine/checkendpoint to test
Example: Backend Service Authentication
// backend-service.ts
import fetch from "node-fetch";
const MANAGER_URL = process.env.MANAGER_URL;
const MACHINE_TOKEN = process.env.MACHINE_USER_TOKEN;
const APP_ID = process.env.APPLICATION_ID;
// Register with Manager
async function registerBackend() {
const response = await fetch(`${MANAGER_URL}/api/machine/register-backend`, {
method: "POST",
headers: {
Authorization: `Bearer ${MACHINE_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
callback_url: "https://my-backend.com/triggers",
version: "1.0.0",
application_id: APP_ID,
}),
});
if (!response.ok) {
throw new Error(`Registration failed: ${response.statusText}`);
}
const data = await response.json();
console.log("Backend registered:", data.registered_id);
}
// Verify authentication
async function checkAuth() {
const response = await fetch(`${MANAGER_URL}/api/machine/check`, {
headers: {
Authorization: `Bearer ${MACHINE_TOKEN}`,
},
});
const data = await response.json();
console.log("Authenticated:", data.authenticated);
}
// Start service
async function start() {
await checkAuth();
await registerBackend();
console.log("Service ready");
}
start();See Also
- Personal Access Tokens Guide - User token management
- Machine Users Guide - Service authentication
- Backend Registration - Connect services
- REST API - Every route and which credential it accepts
- GraphQL API - API usage