Skip to content

API Endpoints

All examples use auth.example.com for single-tenant mode and acme.example.com / _platforms.example.com for multi-tenant mode. Replace with your deployment URL.

Every request requires Authorization: Bearer <access_token>. Mutating requests (POST/PUT/PATCH) require Content-Type: application/json. In multi-tenant mode, include x-tenant-id: <tenant> when targeting a tenant different from the subdomain. See Overview for authentication and pagination details.


Manage OIDC/OAuth2 client applications.

MethodEndpointScopeRate LimitDescription
GET/api/v1/clientsparako:clients:readreadList all clients
POST/api/v1/clientsparako:clients:writewriteCreate a new client
GET/api/v1/clients/:client_idparako:clients:readreadGet client details
PUT/api/v1/clients/:client_idparako:clients:writewriteFull update (replace) client
PATCH/api/v1/clients/:client_idparako:clients:writewritePartial update client
DELETE/api/v1/clients/:client_idparako:clients:deletedeleteDelete client
POST/api/v1/clients/:client_id/activateparako:clients:writewriteActivate a disabled client
POST/api/v1/clients/:client_id/deactivateparako:clients:writewriteDeactivate a client
POST/api/v1/clients/:client_id/secretparako:clients:deletesensitiveRegenerate client secret
GET/api/v1/clients/:client_id/statsparako:clients:readreadGet client usage statistics

Query Parameters:

ParameterTypeDefaultDescription
limitnumber25Items per page (1–100)
afterstringOpaque cursor from previous response
include_countbooleanfalseInclude total_count in response
application_typestringFilter: web, native, or spa
activestringFilter: true or false
qstringFull-text search (max 200 chars)

Response: 200 OK

{
"data": [
{
"client_id": "abc123",
"client_name": "My Web App",
"application_type": "web",
"redirect_uris": ["https://app.example.com/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "client_secret_basic",
"scope": "openid profile email",
"..."
}
],
"pagination": {
"has_more": true,
"next_cursor": "eyJpZCI6IjY1...",
"total_count": 150
}
}

client_secret is stripped from list responses.

Request Body:

FieldTypeRequiredDescription
client_namestringYesClient display name (1–255 chars)
application_typestringNoweb (default), native, or spa
redirect_urisstring[]NoValid URLs for OAuth redirects
post_logout_redirect_urisstring[]NoValid URLs for post-logout redirects
grant_typesstring[]NoOAuth grant types
response_typesstring[]NoOAuth response types
scopestringNoSpace-separated scope string
token_endpoint_auth_methodstringNonone, client_secret_basic, client_secret_post, client_secret_jwt, or private_key_jwt
client_uristringNoValid URL to client website
logo_uristringNoValid URL to client logo
policy_uristringNoValid URL to privacy policy
tos_uristringNoValid URL to terms of service
contactsstring[]NoArray of valid email addresses
descriptionstringNoClient description (max 1000 chars)
tagsstring[]NoArbitrary tags for organization
require_pkcebooleanNoRequire PKCE for this client
id_token_signed_response_algstringNoAlgorithm for ID token signing
subject_typestringNopublic or pairwise
default_max_agenumberNoDefault max auth age (positive integer)
Terminal window
curl -X POST https://auth.example.com/api/v1/clients \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"client_name": "My Web App",
"redirect_uris": ["https://app.example.com/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "client_secret_basic",
"scope": "openid profile email offline_access"
}'

Response: 201 Created

{
"data": {
"client_id": "abc123",
"client_secret": "generated-secret-shown-once",
"client_name": "My Web App",
"application_type": "web",
"redirect_uris": ["https://app.example.com/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"..."
}
}

client_secret is only returned on creation and secret regeneration — store it immediately.

Response: 200 OK — single client object (secret stripped).

Errors: 404 if client not found.

Request Body: Same fields as Create Client — all optional for PATCH, replaces mutable fields for PUT.

Response: 200 OK — updated client object (secret stripped).

Errors: 404 if client not found.

Response: 204 No Content

Errors: 404 if client not found.

Request Body: None.

Response: 200 OK — updated client object (secret stripped).

Errors: 404 if client not found.

Terminal window
curl -X POST https://auth.example.com/api/v1/clients/CLIENT_ID/secret \
-H "Authorization: Bearer TOKEN"

Request Body: None.

Response: 200 OK

{
"data": {
"client_id": "abc123",
"client_secret": "new-secret-shown-once"
}
}

The old secret is immediately invalidated.

Response: 200 OK — client usage statistics object.

Errors: 404 if client not found.


Manage user accounts.

MethodEndpointScopeRate LimitDescription
GET/api/v1/usersparako:users:readreadList all users
POST/api/v1/usersparako:users:writewriteCreate a new user
GET/api/v1/users/:user_idparako:users:readreadGet user details
PUT/api/v1/users/:user_idparako:users:writewriteFull update user
PATCH/api/v1/users/:user_idparako:users:writewritePartial update user
DELETE/api/v1/users/:user_idparako:users:deletedeleteAnonymize or delete user
POST/api/v1/users/:user_id/lockparako:users:writewriteLock user account
DELETE/api/v1/users/:user_id/lockparako:users:writewriteUnlock user account
POST/api/v1/users/:user_id/password-resetparako:users:writesensitiveAdmin password reset
POST/api/v1/users/:user_id/mfa/resetparako:users:writesensitiveReset user MFA
GET/api/v1/users/:user_id/activitiesparako:users:readreadGet user activity log
GET/api/v1/users/:user_id/sessionsparako:sessions:readreadGet user active sessions

Query Parameters:

ParameterTypeDefaultDescription
limitnumber25Items per page (1–100)
afterstringOpaque cursor from previous response
include_countbooleanfalseInclude total_count in response
account_enabledstringFilter: true or false
rolestringFilter by role (max 50 chars)
auth_providerstringFilter by auth provider (max 50 chars)
qstringSearch email, username, name (max 200 chars)

Response: 200 OK — paginated user list.

Sensitive fields stripped: password, hashedPassword, mfa.secret, mfa.recovery_codes, webauthn.credentials.

Request Body:

FieldTypeRequiredDescription
emailstringYesValid email address
passwordstringYesUser password (8–128 chars)
usernamestringNoUsername (1–100 chars)
given_namestringNoFirst name (max 100 chars)
family_namestringNoLast name (max 100 chars)
namestringNoFull display name (max 200 chars)
nicknamestringNoNickname (max 100 chars)
rolestringNoUser role
account_enabledbooleanNoWhether the account is active
Terminal window
curl -X POST https://auth.example.com/api/v1/users \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"email": "user@example.com",
"password": "SecurePassword123!",
"given_name": "Jane",
"family_name": "Doe",
"role": "user"
}'

Response: 201 Created — created user (sensitive fields stripped).

Response: 200 OK — single user object (sensitive fields stripped).

Errors: 404 if user not found.

Request Body: Same fields as Create User except password — all optional, plus email is updatable. PATCH only modifies supplied fields; PUT replaces all mutable fields.

Response: 200 OK — updated user (sensitive fields stripped).

Errors: 404 if user not found.

Response: 204 No Content — user account is anonymized.

Errors: 404 if user not found.

Request Body: None.

Terminal window
# Lock
curl -X POST https://auth.example.com/api/v1/users/USER_ID/lock \
-H "Authorization: Bearer TOKEN"
# Unlock
curl -X DELETE https://auth.example.com/api/v1/users/USER_ID/lock \
-H "Authorization: Bearer TOKEN"

Response: 200 OK — updated user (sensitive fields stripped).

Errors: 404 if user not found.

Request Body:

FieldTypeRequiredDescription
new_passwordstringYesNew password (8–128 chars)
Terminal window
curl -X POST https://auth.example.com/api/v1/users/USER_ID/password-reset \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"new_password": "NewSecurePassword456!"}'

Response: 200 OK

{
"data": {
"message": "Password has been reset"
}
}

Errors: 404 if user not found.

Request Body: None.

Response: 200 OK

{
"data": {
"message": "MFA has been reset"
}
}

Disables TOTP and clears recovery codes.

Errors: 404 if user not found.

Query Parameters: limit, after, include_count (standard pagination).

Response: 200 OK — paginated activity log entries.

Errors: 404 if user not found.

Response: 200 OK

{
"data": [...]
}

Returns [] if the OIDC adapter does not support session listing.

Errors: 404 if user not found.


Manage active OIDC sessions.

MethodEndpointScopeRate LimitDescription
GET/api/v1/sessionsparako:sessions:readreadList all sessions
GET/api/v1/sessions/:jtiparako:sessions:readreadGet session details
DELETE/api/v1/sessions/:jtiparako:sessions:revokedeleteRevoke a session
DELETE/api/v1/sessionsparako:sessions:revokedeleteBulk revoke sessions

Query Parameters:

ParameterTypeDefaultDescription
limitnumber25Items per page (1–100)
afterstringOpaque cursor from previous response
include_countbooleanfalseInclude total_count in response
usernamestringFilter by account ID (max 255 chars)
client_idstringFilter by client ID (max 255 chars)
activestringFilter: true or false

Response: 200 OK — paginated session list.

Response: 200 OK — single session object.

Errors: 404 if session not found.

Response: 204 No Content

Errors: 404 if session not found.

At least one filter is required to prevent mass revocation.

Query Parameters:

ParameterTypeRequiredDescription
usernamestringAt least oneFilter by account ID
client_idstringAt least oneFilter by client ID
Terminal window
curl -X DELETE "https://auth.example.com/api/v1/sessions?username=user@example.com" \
-H "Authorization: Bearer TOKEN"

Response: 200 OK

{
"data": {
"revoked_count": 5
}
}

Errors: 422 if neither username nor client_id is provided.


Manage JSON Web Key Sets for token signing.

MethodEndpointScopeRate LimitDescription
GET/api/v1/jwksparako:jwks:readreadList all signing keys
GET/api/v1/jwks/:kidparako:jwks:readreadGet key details
POST/api/v1/jwks/rotateparako:jwks:rotatesensitiveRotate signing keys
POST/api/v1/jwks/retire-expiredparako:jwks:rotatesensitiveRetire all expired keys
DELETE/api/v1/jwks/:kidparako:jwks:rotatesensitiveRetire a specific key

Query Parameters:

ParameterTypeDescription
statusstringFilter: active, expiring, or retired

Response: 200 OK — flat array (no pagination).

{
"data": [
{
"kid": "key-id-123",
"alg": "RS256",
"use": "sig",
"status": "active",
"promoted": true,
"publicKey": { "kty": "RSA", "n": "...", "e": "AQAB" },
"createdAt": "2024-01-01T00:00:00.000Z",
"rotatedAt": "2024-04-01T00:00:00.000Z"
}
]
}

Private key material is never exposed.

Response: 200 OK — single key object (same structure as above).

Errors: 404 if key not found.

Request Body: None.

Terminal window
curl -X POST https://auth.example.com/api/v1/jwks/rotate \
-H "Authorization: Bearer TOKEN"

Response: 200 OK

{
"data": {
"message": "Keys rotated successfully",
"promoted": 1
}
}

New keys are generated; old keys remain valid during the overlap window. A jwks:rotated event is published to Redis.

Request Body: None.

Response: 200 OK

{
"data": {
"message": "Expired keys retired",
"retired": 1
}
}

Response: 202 Accepted

{
"data": {
"message": "Key 'key-id-123' has been marked for retirement and will be retired at the next rotation cycle",
"kid": "key-id-123",
"current_status": "active"
}
}

Errors: 404 if key not found. 409 if key is already retired.


Query the activity audit trail.

MethodEndpointScopeRate LimitDescription
GET/api/v1/auditparako:audit:readreadList audit log entries
GET/api/v1/audit/:idparako:audit:readreadGet a single audit entry
GET/api/v1/audit/typesparako:audit:readreadGet distinct activity types
GET/api/v1/audit/statsparako:stats:readreadGet audit aggregate statistics

Query Parameters:

ParameterTypeDefaultDescription
limitnumber25Items per page (1–100)
afterstringOpaque cursor from previous response
include_countbooleanfalseInclude total_count in response
typestringFilter by event type (exact match)
statusstringFilter: success, failed, info, or warning
usernamestringFilter by actor username
client_idstringFilter by client ID
fromstringStart date (ISO 8601 datetime)
tostringEnd date (ISO 8601 datetime)
Terminal window
curl "https://auth.example.com/api/v1/audit?type=login&status=failed&from=2024-01-01T00:00:00Z&limit=50" \
-H "Authorization: Bearer TOKEN"

Response: 200 OK — paginated audit entries.

Response: 200 OK — single audit entry.

Errors: 404 if entry not found.

Response: 200 OK — flat array of distinct type strings (no pagination).

{
"data": ["login", "logout", "password_reset", "mfa_enabled", "..."]
}

Response: 200 OK

{
"data": {
"totalActivities": 1000,
"uniqueUsers": 50,
"todayCount": 25,
"successfulLogins": 20,
"failedLogins": 5
}
}

Dashboard overview and system health.

MethodEndpointScopeRate LimitDescription
GET/api/v1/statsparako:stats:readreadAggregate dashboard overview
GET/api/v1/stats/healthparako:stats:readreadSystem health check

Response: 200 OK

{
"data": {
"users": { "total": 100 },
"clients": { "total": 25 },
"sessions": { "..." },
"grants": { "..." },
"activity": {
"totalActivities": 1000,
"uniqueUsers": 50,
"todayCount": 25,
"successfulLogins": 20,
"failedLogins": 5
}
}
}

Each section is isolated — if one fails, others still return. Failed sections include an error field instead.

Terminal window
curl https://auth.example.com/api/v1/stats/health \
-H "Authorization: Bearer TOKEN"

Response: 200 OK (or 503 Service Unavailable if degraded)

{
"data": {
"status": "healthy",
"checks": {
"database": { "status": "healthy" },
"oidc": { "status": "healthy" },
"config": { "status": "healthy" }
},
"timestamp": "2024-01-01T00:00:00.000Z"
}
}

Each check status is healthy, unhealthy, or unknown. If any check is unhealthy, the overall status is degraded and the HTTP status is 503.


Manage Dynamic Client Registration (DCR) initial access tokens.

MethodEndpointScopeRate LimitDescription
GET/api/v1/registration-tokensparako:registration-tokens:readreadList active tokens
POST/api/v1/registration-tokensparako:registration-tokens:writewriteCreate a new token
GET/api/v1/registration-tokens/:jtiparako:registration-tokens:readreadGet token details
DELETE/api/v1/registration-tokens/:jtiparako:registration-tokens:deletedeleteRevoke a token

Request Body:

FieldTypeRequiredDescription
expires_innumberYesToken lifetime in seconds (300–2,592,000 = 5 min to 30 days)
max_usage_countnumberYesMax client registrations allowed (1–1,000)
policiesstring[]NoRegistration policies (1–10 items, 1–128 chars each). Default: ["general-policy"]
notestringNoAdmin note for identifying the token (max 500 chars)
Terminal window
curl -X POST https://auth.example.com/api/v1/registration-tokens \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{
"expires_in": 86400,
"max_usage_count": 5,
"note": "CI/CD pipeline token"
}'

Response: 201 Created

{
"data": {
"jti": "token-id",
"token": "raw-token-value-shown-once",
"expires_at": "2024-01-08T00:00:00.000Z",
"max_usage_count": 5,
"current_usage_count": 0,
"policies": ["general-policy"],
"note": "CI/CD pipeline token",
"created_at": "2024-01-01T00:00:00.000Z"
}
}

The raw token value is only returned on creation — store it immediately. Use it in the Authorization: Bearer header when calling POST /oidc/v1/register-rp.

Response: 200 OK — token metadata without the raw token value.

Errors: 404 if token not found.

Response: 200 OK — paginated list.

Response: 204 No Content

Errors: 404 if token not found.


Manage tenants in multi-tenant deployments. Requires platform-only scopes. Tokens must be issued by the _platforms tenant issuer (e.g. https://_platforms.example.com/oidc/v1).

These endpoints are only available in multi-tenant mode. In single-tenant mode, there are no tenants to manage.

MethodEndpointScopeRate LimitDescription
GET/api/v1/tenantsparako:tenants:readreadList all tenants
POST/api/v1/tenantsparako:tenants:writewriteCreate a new tenant
GET/api/v1/tenants/:slugparako:tenants:readreadGet tenant details
GET/api/v1/tenants/:slug/configparako:cross-tenant:readreadGet tenant config overrides
PUT/api/v1/tenants/:slug/config/:sectionparako:cross-tenant:writewriteUpdate a tenant config section

Query Parameters:

ParameterTypeDefaultDescription
limitnumber25Items per page (1–100)
afterstringOpaque cursor from previous response
statusstringFilter by tenant status

Response: 200 OK — paginated tenant list.

Request Body:

FieldTypeRequiredDescription
slugstringYesUnique identifier (2–63 chars, lowercase alphanumeric with hyphens, cannot start/end with hyphen)
display_namestringYesDisplay name (1–255 chars)
domainstringNoCustom domain for the tenant
Terminal window
curl -X POST https://_platforms.example.com/api/v1/tenants \
-H "Authorization: Bearer PLATFORM_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"slug": "acme",
"display_name": "Acme Corp"
}'

Response: 201 Created — created tenant object.

Errors: 409 Conflict if slug already exists.

Response: 200 OK — single tenant object.

Errors: 404 if tenant not found.

Terminal window
curl https://_platforms.example.com/api/v1/tenants/acme/config \
-H "Authorization: Bearer PLATFORM_TOKEN"

Response: 200 OK — configuration overrides object (or {} if none set).

Errors: 404 if tenant not found.

Path Parameters:

ParameterDescription
:slugTenant slug
:sectionOne of: application, branding, security, features, oidc, integrations, notifications

Request Body: JSON object with the section fields to override.

Terminal window
curl -X PUT https://_platforms.example.com/api/v1/tenants/acme/config/branding \
-H "Authorization: Bearer PLATFORM_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"companyName": "Acme Corp",
"logo": "/uploads/acme-logo.png"
}'

Response: 200 OK — updated configuration.

Errors:

  • 404 if tenant not found.
  • 422 if section is not in the allowed list.

All errors follow RFC 9457 Problem Detail format. See Overview — Error Handling for the full error type reference.

Validation errors include an errors array:

{
"type": "urn:parako:error:validation",
"title": "Validation Error",
"status": 422,
"detail": "Request validation failed",
"instance": "/api/v1/users",
"errors": [
{ "field": "email", "message": "Invalid email format" },
{ "field": "password", "message": "Password must be at least 8 characters" }
]
}