TryMellon
Navigation

Admin REST API

Server-to-server endpoints for managing users, credentials, audit logs, and usage. Requires an OAuth2 bearer token.

Admin REST API

Server-to-server (S2S) endpoints for managing your application’s users, passkeys, and data. All endpoints require an OAuth2 bearer token — not the publishable key.


Authentication

All admin endpoints require Authorization: Bearer <token> obtained from the OAuth2 client credentials flow.

curl -X POST https://api.trymellonauth.com/oauth/token \
  -H "Content-Type: application/json" \
  -d '{
    "client_id": "YOUR_APP_ID",
    "client_secret": "YOUR_APP_SECRET",
    "grant_type": "client_credentials"
  }'

Response:

{ "access_token": "eyJ...", "token_type": "Bearer", "expires_in": 3600 }

Use this token in the Authorization header for all admin requests. The token is scoped to one application.

Keep the client_secret server-side only. Never expose it in browser code or client-side bundles. Use the publishableKey (cli_xxxx) for SDK calls from the browser.


Response envelope

All endpoints return a consistent JSON envelope:

{ "ok": true, "data": { ... } }           // success
{ "ok": false, "error": { "code": "...", "message": "..." } }  // error

Users

Create user

POST /v1/users

Pre-registers a user in your application before they register a passkey. Useful for seeding users from an existing system.

Supports idempotency: pass Idempotency-Key: <uuid> to safely retry without creating duplicates.

Request:

curl -X POST https://api.trymellonauth.com/v1/users \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: a1b2c3d4-..." \
  -d '{ "external_user_id": "user_123" }'

Body:

FieldTypeDescription
external_user_idstring (required)Your user’s ID. 1–255 chars.

Response 201:

{
  "ok": true,
  "data": {
    "external_user_id": "user_123",
    "created_at": "2026-04-07T12:00:00Z",
    "updated_at": "2026-04-07T12:00:00Z"
  }
}

Get user

GET /v1/users/:external_user_id

Response 200:

{
  "ok": true,
  "data": {
    "id": "uuid",
    "external_user_id": "user_123",
    "status": "active",
    "created_at": "2026-04-07T12:00:00Z",
    "updated_at": "2026-04-07T12:00:00Z"
  }
}

Returns 404 if the user does not exist.


List users

GET /v1/users

Cursor-based pagination.

Query parameters:

ParameterTypeDescription
limitintegerResults per page. Default 50, max 100.
starting_afterUUIDCursor from the previous page’s next_cursor.

Response 200:

{
  "ok": true,
  "data": {
    "data": [
      { "id": "uuid", "external_user_id": "user_123", "status": "active", "created_at": "..." }
    ],
    "has_more": true,
    "next_cursor": "uuid-of-last-item"
  }
}

Delete user

DELETE /v1/users/:external_user_id

Deletes the user and cascades to all their passkeys, sessions, and audit log entries. This operation is irreversible.

Response 204: No content on success.


Credentials (Passkeys)

List credentials for a user

GET /v1/users/:external_user_id/credentials

Returns all passkeys registered by the user, with status and device information.

Query parameters:

ParameterTypeDescription
pageintegerPage number (offset pagination). Default 1.
limitintegerResults per page.

Response 200:

{
  "ok": true,
  "data": {
    "credentials": [
      {
        "credential_id": "cred_abc123",
        "status": "active",
        "alias": "Touch ID — iPhone 15",
        "transports": ["internal"],
        "attestation_type": "none",
        "created_at": "2026-04-01T10:00:00Z",
        "last_used_at": "2026-04-07T08:30:00Z"
      }
    ],
    "pagination": { "page": 1, "limit": 20, "total": 1 }
  }
}

Credential statuses: active, revoked.


Revoke a credential

DELETE /v1/users/:external_user_id/credentials/:credential_id

Revokes a specific passkey. The user will not be able to authenticate with it. Other passkeys registered by the user remain active.

Use this when a user reports a lost device or suspects a compromised authenticator.

Response 204: No content on success.

curl -X DELETE \
  "https://api.trymellonauth.com/v1/users/user_123/credentials/cred_abc123" \
  -H "Authorization: Bearer <token>"

Audit Logs

List audit logs

GET /v1/audit-logs

Returns a paginated stream of security events for your application.

Query parameters:

ParameterTypeDescription
limitintegerResults per page. Default 50, max 100.
starting_afterUUIDCursor from the previous page’s next_cursor.
eventstringFilter by event type (see below).
successbooleanFilter by outcome (true or false).

Event types:

The filter accepts every audit event the backend persists. The canonical list is the AUDIT_EVENT_VALUES array in core/domain/audit/value-objects/audit-event.vo.ts; the route enum is sourced from that constant so adding an event in the domain VO automatically opens it for filtering here. The full set today:

ValueCategoryDescription
user.createdoperationA user was created
user.deletedoperationA user was deleted
passkey.registeredoperationA passkey was registered
auth.successoperationAn authentication succeeded
auth.failedsecurityAn authentication failed
credential.revokedsecurityA passkey was revoked
credential.lockoutsecurityA credential was locked after excessive failed attempts
credential.replay_detectedsecurityA sign-count anomaly was detected (possible replay)
account.recoveredsecurityAn account was recovered via OTP
onboarding.completedoperationAn onboarding session completed
registration.startedoperationA registration ceremony started
subscription.createdbillingA subscription was created
subscription.updatedbillingA subscription plan changed
subscription.cancelledbillingA subscription was cancelled
subscription.expiredbillingA subscription expired (non-renewal)
account.plan.changedbillingAn account’s plan tier changed
action.challenge.issuedoperationAn action-signing challenge was issued
action.signedsecurityAn action was signed and verified
action.sign.failedsecurityAn action-signing verification failed
attestation.policy.updatedoperationAn attestation policy was changed
attestation.policy.violationsecurityA credential violated the active attestation policy
attestation.credential.blockedsecurityA credential was blocked by attestation enforcement
attestation.mds.cache.refreshedoperationThe FIDO MDS cache was refreshed
attestation.mds.cache.failedoperationThe FIDO MDS cache refresh failed
dbsc.session.registeredoperationA device-bound session (DBSC) was registered
dbsc.session.verifiedoperationA DBSC session token was verified
dbsc.session.invalidatedoperationA DBSC session was invalidated
dbsc.verification.failedsecurityDBSC verification failed
identifier.linkedoperationAn email or wallet identifier was linked to a user
identifier.unlinkedoperationAn identifier was unlinked from a user
session.exchangedoperationA maintainer session was exchanged via OAuth2 token-exchange
session.exchange.deniedsecurityA token-exchange call was denied (policy / membership)
acting-as.invokedsecurityAn actor invoked an endpoint while acting on behalf of a user
acting-as.endedsecurityAn acting-as session ended
application.config_changedoperationApplication settings were updated
application.secret_rotatedoperationApplication client secret was rotated
authorization.deniedsecurityA policy decision returned DENY (Principal ADT, ADR-064)

Response 200:

{
  "ok": true,
  "data": {
    "data": [
      {
        "id": "uuid",
        "event": "auth.success",
        "success": true,
        "user_id": "uuid",
        "ip_address": "203.0.113.42",
        "metadata": {},
        "created_at": "2026-04-07T09:15:00Z"
      }
    ],
    "has_more": false,
    "next_cursor": null
  }
}

Retention by plan: Starter and Growth — 90 days. Scale and Enterprise — 1 year+. Logs beyond the retention window are automatically purged. Contact support for extended retention options.


Usage

Get usage

GET /v1/usage?period=YYYY-MM

Returns API call counts and active user counts for a billing period.

Query parameters:

ParameterTypeDescription
periodstring (required)Billing month in YYYY-MM format. E.g. 2026-04.

Response 200:

{
  "ok": true,
  "data": {
    "tenant_id": "uuid",
    "period": "2026-04",
    "api_calls": 4820,
    "active_users": 312
  }
}

Privacy / GDPR

Get user data report

GET /v1/privacy/user-data/:external_user_id

Returns all data stored by TryMellon for a specific user. Use this to fulfill GDPR Article 15 (right of access) requests.

The report includes: user profile, registered credentials, session history, and audit log entries.

curl "https://api.trymellonauth.com/v1/privacy/user-data/user_123" \
  -H "Authorization: Bearer <token>"

To delete all data for a user (GDPR Article 17 — right to erasure), use Delete user. Deletion cascades to all associated data.


Rate limits

Admin endpoints are rate-limited per OAuth2 token. On exceeding the limit, the API returns 429 Too Many Requests with a Retry-After header. The SDK’s retry logic handles 429 with exponential backoff automatically; for raw HTTP clients, respect the Retry-After value.


Error responses

Errors share the envelope { "ok": false, "error": { "code": "...", "message": "..." } } and a matching HTTP status.

404 application_not_found

Returned on any application endpoint (GET /v1/applications/:id, PATCH /v1/applications/:id, POST /v1/applications/:id/rotate-secret) when either:

  • the application does not exist, or
  • it exists but your credentials do not authorize access to it — i.e. you are not the creator, nor an owner of its tenant.

TryMellon returns the same status code for both cases to prevent information disclosure across tenants. This is a deliberate design (ADR-064): an attacker probing for the existence of an app in a foreign tenant sees the same 404 as a missing app.

Operators can correlate internally via the authorization.denied audit trail, which captures the policy name, principal kind (user / service), principal identity, and the resource’s tenant. See Tenants vs accounts for the authorization model.

401 unauthorized

The request lacks a valid Bearer token, a session cookie, or Basic credentials. Fresh-auth flows (e.g. recovery) may also surface 401 when the flow is half-complete.

403 forbidden

Reserved for operations where the caller kind is wrong for the scope — e.g. requesting GET /v1/applications?scope=account with service credentials returns 403 with Account scope requires a user session; service credentials are tenant-scoped.. Authorization denials on per-resource endpoints collapse to 404 (see above) to preserve tenant opacity.

400 validation_error

Body or query did not validate against the schema. The error.message names the offending field.