Skip to content

Tenants and API keys

A tenant is an organisation using Paprika: its templates, fonts, brand kits, outputs, keys and usage are its own, and no other tenant’s requests can reach them. Its systems and people use API keys, each acting for the tenant within its scopes. The admin API, under /v1/admin, makes and manages both. The operator operations are in the API reference.

The admin API takes the token in PAPRIKA_ADMIN_TOKEN instead of an API key. Several can be listed, comma-separated, so a new one can be brought in before the old one is removed; without one the admin API is off. Keep it off the internet (security).

The examples use the API’s address and the token:

Terminal window
export PAPRIKA_URL=http://localhost:4179
export PAPRIKA_ADMIN_TOKEN=…

Without the token, or with a wrong one, the answer is 401:

Terminal window
curl "$PAPRIKA_URL/v1/admin/tenants"
{
"code": "admin-unauthorized",
"message": "A valid admin token is required: Authorization: Bearer …",
"requestId": "fab35336"
}

Make a tenant with an id (lower-case letters, digits and dashes, fixed for good) and a name:

Terminal window
curl -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" -H "Content-Type: application/json" -d '{"id": "fabrikam", "name": "Fabrikam"}' "$PAPRIKA_URL/v1/admin/tenants"
{
"id": "fabrikam",
"name": "Fabrikam",
"tier": null,
"retentionDays": null,
"createdAt": "2026-11-10T15:51:56.007Z",
"suspendedAt": null,
"deletedAt": null
}

An id taken already is 409 tenant-exists. List every tenant, local (the editor’s) among them:

Terminal window
curl -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" "$PAPRIKA_URL/v1/admin/tenants"

Change a tenant’s name, how many days its unused uploads are kept (retentionDays, 30 when null) or its tier (scheduler):

Terminal window
curl -X PATCH -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" -H "Content-Type: application/json" -d '{"retentionDays": 90}' "$PAPRIKA_URL/v1/admin/tenants/fabrikam"

Issue a key with a name and its scopes, and an expiresAt if it should stop working on a date:

Terminal window
curl -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" -H "Content-Type: application/json" -d '{"name": "website", "scopes": ["render"], "expiresAt": "2030-01-01T00:00:00Z"}' "$PAPRIKA_URL/v1/admin/tenants/fabrikam/keys"
{
"key": "ppk_Vb7Qm2Rk9TxW_s3cr3tExampleOnly",
"id": "Vb7Qm2Rk9TxW",
"tenant": "fabrikam",
"name": "website",
"scopes": ["render"],
"createdAt": "2026-11-10T15:51:56.052Z",
"expiresAt": "2030-01-01T00:00:00.000Z",
"revokedAt": null,
"lastUsedAt": null,
"rotatedTo": null,
"tier": null
}

The secret, key, is shown only in this answer. The scopes are:

Scope Allows
render Reading templates and versions, rendering, jobs, batches, outputs and named resources, reading the font library
publish Making templates and versions, changing the font library and brand kits
keys The tenant’s own keys and webhooks

A tenant with a keys key manages its own keys from then on (authentication), never giving one more scopes than its own. List a tenant’s keys, with when each was last used:

Terminal window
curl -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" "$PAPRIKA_URL/v1/admin/tenants/fabrikam/keys"

Rotate a key to replace it without an outage: the answer is a new key, and the old one keeps working for 24 hours.

Terminal window
curl -X POST -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" "$PAPRIKA_URL/v1/admin/tenants/fabrikam/keys/Vb7Qm2Rk9TxW/rotate"

Revoke one that may have leaked: it stops working at once.

Terminal window
curl -X DELETE -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" "$PAPRIKA_URL/v1/admin/tenants/fabrikam/keys/Vb7Qm2Rk9TxW"

Suspending a tenant stops its keys at once and cancels its queued jobs; its data stays. Resuming gives its keys back:

Terminal window
curl -X POST -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" "$PAPRIKA_URL/v1/admin/tenants/fabrikam/suspend"
Terminal window
curl -X POST -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" "$PAPRIKA_URL/v1/admin/tenants/fabrikam/resume"

Deleting a tenant refuses its keys at once too. For 30 days it can be restored as it was; after that the hourly sweep purges every row and file of it, keeping only its usage for the last bill (metering).

Terminal window
curl -X DELETE -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" "$PAPRIKA_URL/v1/admin/tenants/fabrikam"
Terminal window
curl -X POST -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" "$PAPRIKA_URL/v1/admin/tenants/fabrikam/restore"
{
"id": "fabrikam",
"name": "Fabrikam",
"tier": null,
"retentionDays": 90,
"createdAt": "2026-11-10T15:51:56.007Z",
"suspendedAt": null,
"deletedAt": null
}

The editor’s tenant, local, can’t be deleted:

Terminal window
curl -X DELETE -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" "$PAPRIKA_URL/v1/admin/tenants/local"
{
"code": "bad-request",
"message": "Bad request: the local tenant cannot be deleted",
"requestId": "d60827d6"
}

Every change to tenants and keys, by the admin or by a tenant’s own keys key, and every font rights assertion (fonts) is written to the audit log, kept 400 days. Read a tenant’s, newest first:

Terminal window
curl -H "Authorization: Bearer $PAPRIKA_ADMIN_TOKEN" "$PAPRIKA_URL/v1/admin/audit?tenant=fabrikam"
[
{
"at": "2026-11-10T15:49:12.358Z",
"tenant": "fabrikam",
"actor": "admin",
"action": "tenant.restored",
"target": "fabrikam",
"details": null,
"requestId": "e7cb5dd9"
}
]