Management API
Create and manage applications, API resources, secrets, and custom domains.
Authentication
The management API uses AuthKnox itself for auth — the same client credentials flow your services use. During onboarding, AuthKnox creates a management application in your tenant and returns its credentials. Use those credentials to get a JWT targeting https://management.authknox.com, then send that JWT as a Bearer token on every management request.
curl -X POST https://{tenantID}.authknox.com/token \ -H "Content-Type: application/json" \ -d '{ "grant_type": "client_credentials", "client_id": "your-mgmt-app-id", "client_secret": "your-mgmt-app-secret", "audience": "https://management.authknox.com" }'
curl https://{tenantID}.authknox.com/applications \ -H "Authorization: Bearer eyJhbGci..."
Important: always use the native subdomain
The JWT must be obtained from https://{tenantID}.authknox.com/token,
not a custom domain. The management API validates the JWT iss
claim against the native subdomain issuer. A token obtained via a custom domain carries a different
iss and will be rejected with 401.
Required scopes
Each endpoint requires a specific scope in the bearer token. Request only the scopes your integration needs.
applications:read List or get applicationsapplications:create Create applicationsapplications:delete Delete applicationsapplications:rotate Rotate or invalidate secretsapis:read List or get API resourcesapis:create Register API resourcesapis:delete Delete API resourcesdomains:read Get custom domain statusdomains:create Register or confirm DNS for a custom domaindomains:delete Delete a custom domainAuth errors
// 401 — missing or invalid token { "error": "unauthorized", "error_description": "missing or malformed Authorization header" } { "error": "unauthorized", "error_description": "invalid or expired token" } // 403 — authenticated but wrong tenant or missing scope { "error": "forbidden" } { "error": "forbidden", "error_description": "scope \"applications:create\" required" }
Applications
An application represents a machine identity — a service or job that needs to request tokens. Each application has a client_id, a secret (hashed with argon2id), and a set of grants defining which API resources it can request tokens for.
/applications
applications:readReturns a paginated list of applications. Results are sorted alphabetically by client_id.
curl "https://{tenantID}.authknox.com/applications?page_size=20" \ -H "Authorization: Bearer $MGMT_TOKEN"
{
"applications": [
{
"client_id": "billing-service",
"name": "Billing Service",
"enabled": true,
"created_at": "2024-05-01T10:00:00Z",
"api_grants": [
{
"audience": "https://payments.acme.com",
"scopes": ["payments:read", "payments:write"]
}
]
}
],
"next_page_token": "dGVzdA"
}
| page_size | optional | Number of results per page. Integer 1–100, default 20. Non-integer or out-of-range values return 400. |
| page_token | optional | Opaque cursor from a previous response's next_page_token. Absent on first page. next_page_token is omitted (not null) when there are no more results. |
/applications/{clientID}
applications:readReturns a single application. Secrets are never included in GET responses.
curl https://{tenantID}.authknox.com/applications/billing-service \ -H "Authorization: Bearer $MGMT_TOKEN"
{
"client_id": "billing-service",
"name": "Billing Service",
"enabled": true,
"created_at": "2024-05-01T10:00:00Z",
"api_grants": [...]
}
/applications
applications:createCreates an application and returns the client_secret once. Store it immediately — it cannot be retrieved again. The response includes Cache-Control: no-store per RFC 6749 §5.1.
curl -X POST https://{tenantID}.authknox.com/applications \ -H "Authorization: Bearer $MGMT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "client_id": "billing-service", "name": "Billing Service", "api_grants": [ { "audience": "https://payments.acme.com", "scopes": ["payments:read", "payments:write"] } ] }'
{
"client_id": "billing-service",
"name": "Billing Service",
"enabled": true,
"created_at": "2024-05-01T10:00:00Z",
"api_grants": [...],
"client_secret": "cs_live_..."
}
// Cache-Control: no-store
// Pragma: no-cache
| client_id | required | Unique identifier for this application. Used as the sub claim in issued tokens. Immutable after creation. |
| name | required | Human-readable display name. |
| api_grants | optional | Array of grants. Each grant has an audience (must be a pre-registered API resource) and a scopes array. Max 10 grants; max 30 scopes per grant; max 48 chars per scope token. |
/applications/{clientID}
applications:deletePermanently deletes an application and removes it from the global client index. Tokens already issued remain valid until they expire.
curl -X DELETE \ https://{tenantID}.authknox.com/applications/billing-service \ -H "Authorization: Bearer $MGMT_TOKEN"
Returns 204 No Content on success.
/applications/{clientID}/rotate-secret
applications:rotateGenerates a new secret and optionally keeps the old one valid for a transition window. Set previous_secret_ttl_seconds to a non-zero value to allow both secrets to be accepted simultaneously during deployment. Set it to 0 for an immediate hard rotation — the old secret is invalidated at once.
curl -X POST \ https://{tenantID}.authknox.com/applications/billing-service/rotate-secret \ -H "Authorization: Bearer $MGMT_TOKEN" \ -H "Content-Type: application/json" \ -d '{"previous_secret_ttl_seconds": 3600}'
{
"client_id": "billing-service",
"client_secret": "cs_live_..."
}
// Cache-Control: no-store
// Pragma: no-cache
| previous_secret_ttl_seconds | required | How long the old secret remains valid after rotation. 0 = immediate invalidation. 1–604800 (7 days) = grace period. Missing or null returns 400. |
/applications/{clientID}/invalidate-previous-secret
applications:rotateImmediately drops all previous (non-current) secrets for an application. Use this after a grace-period rotation once you have confirmed all instances have picked up the new secret and the transition window can be closed early.
curl -X POST \ https://{tenantID}.authknox.com/applications/billing-service/invalidate-previous-secret \ -H "Authorization: Bearer $MGMT_TOKEN"
Returns 204 No Content on success. Safe to call even if no previous secret exists.
API Resources
An API resource is an audience value that applications can request tokens for. Register a resource server here before granting access to it in any application. The audience field becomes the aud claim in issued tokens.
/apis
apis:readReturns a paginated list of registered API resources. Supports the same page_size and page_token parameters as the applications list.
curl https://{tenantID}.authknox.com/apis \ -H "Authorization: Bearer $MGMT_TOKEN"
{
"apis": [
{
"audience": "https://payments.acme.com",
"name": "Payments API",
"scopes": ["payments:read", "payments:write"],
"enabled": true,
"created_at": "2024-05-01T10:00:00Z"
}
],
"next_page_token": "dGVzdA"
}
/apis/{audience}
apis:readReturns a single API resource by audience value. The audience must be URL-encoded in the path.
curl "https://{tenantID}.authknox.com/apis/https%3A%2F%2Fpayments.acme.com" \ -H "Authorization: Bearer $MGMT_TOKEN"
/apis
apis:createRegisters a new API resource. The audience must be unique within your tenant. Define all scopes this resource supports — applications can only request scopes listed here.
curl -X POST https://{tenantID}.authknox.com/apis \ -H "Authorization: Bearer $MGMT_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "audience": "https://payments.acme.com", "name": "Payments API", "scopes": ["payments:read", "payments:write", "payments:refund"] }'
{
"audience": "https://payments.acme.com",
"name": "Payments API",
"scopes": ["payments:read", "payments:write", "payments:refund"],
"enabled": true,
"created_at": "2024-05-01T10:00:00Z"
}
| audience | required | The audience string for this resource server. A URI is conventional (https://api.yourdomain.com) but any unique string works. Becomes the aud claim in tokens. |
| name | required | Human-readable name for this resource server. |
| scopes | optional | Array of scope strings this resource server accepts. Max 30 scopes; each scope token max 48 characters. Applications can only request scopes defined here. |
/apis/{audience}
apis:deleteDeletes an API resource. The audience must be URL-encoded in the path. Existing application grants referencing this audience are not automatically cleaned up.
curl -X DELETE \ "https://{tenantID}.authknox.com/apis/https%3A%2F%2Fpayments.acme.com" \ -H "Authorization: Bearer $MGMT_TOKEN"
Returns 204 No Content on success.
Custom Domain
By default your token endpoint lives at {tenantID}.authknox.com/token. A custom domain lets your services call auth.yourdomain.com/token instead. TLS is provisioned automatically via Google Certificate Manager. Custom domains are available on paid plans only.
Domain status flow
After you call POST /domain, the provisioner picks up the request within 5 minutes and creates the DNS challenge. You then add two CNAME records to your DNS provider and call POST /domain/confirm-dns. Certificate provisioning runs automatically. If provisioning fails, the status moves to failed — delete the domain and register again to retry.
/domain
domains:createRegisters a custom domain. The provisioner creates the DNS auth challenge within 5 minutes. Poll GET /domain until status is awaiting_dns, then add the returned DNS records and call POST /domain/confirm-dns. Rate limited to 3 registrations per tenant per 24 hours.
curl -X POST https://{tenantID}.authknox.com/domain \ -H "Authorization: Bearer $MGMT_TOKEN" \ -H "Content-Type: application/json" \ -d '{"domain": "auth.yourdomain.com"}'
{
"domain": "auth.yourdomain.com",
"status": "pending_initiation",
"dns_records": [
{
"type": "CNAME",
"name": "auth.yourdomain.com",
"value": "{tenantID}.authknox.com.",
"ttl": 300
}
]
}
The routing CNAME is always returned. The challenge CNAME appears once status reaches awaiting_dns.
/domain
domains:readReturns the current custom domain status. When status is awaiting_dns, both DNS records are included: the routing CNAME and the certificate challenge CNAME. Add both to your DNS provider before confirming.
curl https://{tenantID}.authknox.com/domain \ -H "Authorization: Bearer $MGMT_TOKEN"
{
"domain": "auth.yourdomain.com",
"status": "awaiting_dns",
"dns_records": [
{
"type": "CNAME",
"name": "auth.yourdomain.com",
"value": "{tenantID}.authknox.com.",
"ttl": 300
},
{
"type": "CNAME",
"name": "_acme-challenge.auth.yourdomain.com",
"value": "abc123.authorize.certificatemanager.goog.",
"ttl": 3600
}
]
}
| pending_initiation | Queued for provisioning. The provisioner will create the DNS challenge within 5 minutes. |
| awaiting_dns | DNS challenge is ready. Add both CNAME records to your DNS provider, then call POST /domain/confirm-dns. |
| cert_pending | DNS confirmed. Certificate provisioning is in progress. This can take 5–30 minutes depending on DNS propagation. |
| active | Your custom domain is live. HTTPS traffic to it is served by AuthKnox. |
| failed | Provisioning failed permanently. Delete the domain and register again to retry. |
/domain/confirm-dns
domains:createTells AuthKnox you have added the DNS records and certificate provisioning can proceed. The domain must be in awaiting_dns status. Returns 409 if called in any other state.
curl -X POST https://{tenantID}.authknox.com/domain/confirm-dns \ -H "Authorization: Bearer $MGMT_TOKEN"
{
"domain": "auth.yourdomain.com",
"status": "cert_pending",
"dns_records": [
{
"type": "CNAME",
"name": "auth.yourdomain.com",
"value": "{tenantID}.authknox.com.",
"ttl": 300
}
]
}
/domain
domains:deleteRemoves the custom domain. Routing stops immediately — the domain stops resolving to AuthKnox as soon as this call returns. Certificate Manager resources are cleaned up asynchronously by the provisioner. Can be called in any state.
curl -X DELETE https://{tenantID}.authknox.com/domain \ -H "Authorization: Bearer $MGMT_TOKEN"
Returns 204 No Content on success.
Common errors
All errors follow a consistent shape.
{ "error": "error_code", "error_description": "human-readable detail" }
| 400 | Malformed request body, missing required field, or out-of-range parameter (e.g. page_size not integer, previous_secret_ttl_seconds missing). |
| 401 | Missing, malformed, or expired JWT. Check that the token was obtained from the native subdomain token endpoint. |
| 403 | Authenticated but not authorised: wrong tenant, missing scope, or plan limit reached. |
| 404 | Resource not found. |
| 409 | Conflict: resource already exists, or state transition not allowed (e.g. confirm-dns when not awaiting_dns). |
| 413 | Request body too large. Management endpoints accept up to 18 KB. |
| 429 | Rate limit exceeded. Returned for domain registration when the 3/24h limit is reached. Response includes retry_after_seconds. |
Ready to build?
Free to start. No credit card required. Your first 10,000 tokens are on us.