DEVELOPER DOCUMENTATION
Create Token
Mint a new personal API token. New tokens can never exceed the caller's scopes — the secret is shown once.
/api/public/v1/tokensMints a new ot_pat_ personal API token on the account. This is how you hand a narrowly-scoped token to a sub-agent or integration, set an expiry on automation credentials, and rotate a token (create the replacement, switch over, then revoke the old one).
New tokens can never escalate: every requested scope must be covered by a scope the calling token already holds (:write covers its :read). Accounts hold at most 25 active tokens.
This endpoint always mints fixed fine-grained scopes — the explicit list you request, or a clone of the caller’s current scopes. Full-access keys, which automatically expand to current and future self-serve scopes, can only be created in Developer Settings in the web app; the API cannot mint one.
The plaintext token is returned only in this response — store it immediately. Afterwards only the masked preview is visible via GET /tokens.
Requirements: any valid token — token management needs no specific scope or feature flag, and works pre-claim.
Request
Section titled “Request”All fields are optional — an empty body mints a no-expiry clone of the caller’s scopes named “API token”.
namestringbodyLabel for the token (max 120 chars). Defaults to API token.
scopesstring[]bodyScopes for the new token, from the scope catalog. Defaults to the calling token’s scopes. Every entry must be covered by the caller’s scopes (403 otherwise).
expiresAtstringbodyISO 8601 timestamp in the future after which the token stops working. Defaults to no expiry.
Response
Section titled “Response”Returns 201.
tokenstringThe plaintext ot_pat_… secret. Shown once — store it now.
tokenTypestringbearer.
metadataobjectThe token record: {id, name, preview, scopes, status: "active", organizationId, createdAt, lastUsedAt, expiresAt, revokedAt} — same shape as the entries in GET /tokens.
Errors
Section titled “Errors”| Status | code | Meaning |
|---|---|---|
400 | BAD_REQUEST | Body not valid JSON; name empty or over 120 chars; scopes not a string array or contains unknown scopes (details.unknownScopes, details.supportedScopes); expiresAt not a valid ISO timestamp or not in the future |
401 | UNAUTHORIZED | Missing or invalid token |
403 | FORBIDDEN | Requested scopes exceed the caller’s (details.requestedScopes, details.grantedScopes, details.escalatedScopes) |
409 | CONFLICT | Active token limit reached (25) — revoke unused tokens first |