---
title: "Exchange application credentials for a short-lived widget JWT"
method: POST
path: "/tokens"
tags: ["Tokens"]
---

# Exchange application credentials for a short-lived widget JWT

`POST /tokens`

## Request

- Base URL: `https://api.customdomain.ai/v1`
- URL: `https://api.customdomain.ai/v1/tokens`
- Auth: none required

Server-side only. Trade an application's `client_secret` for a short-lived widget JWT the browser can use on the Connect endpoints. Never ship the `client_secret` or an API key to the browser.

## Request body

- object
  - `application_id` string, required
  - `client_secret` string, required
  - `domain` string: Optional. Binds the token to a single hostname.
  - `domains` string[]: Optional. A list of hostnames the token may act on, so a multi-domain flow needs one token instead of one per domain. Each entry is normalized and duplicates and blanks are dropped. Additive to `domain`; omit both for a token that is not bound to a hostname.
  - `end_user_ref` string: Optional. Binds the token to one end user: your own id for them. A connection created with the token is stamped with it (a different `end_user_ref` in the create body is refused), and an end user's token removes a domain, or reads its monitoring, services and timeline, only when it is bound to the domain and minted with the connection's own `end_user_ref`.
  - `end_user_email` string, email: Optional. The email address of the end user this token is for. It is never put in the token: it is kept on our side and a connection created with this token takes it, so that after a Move the person gets the new DNS records by email on your behalf. An `end_user_email` on the connection create wins over it.
  - `scope` 'connect' | 'app': `connect` (the default) is an END USER's token for the widget: it runs the connect flow for a domain (check, create, read its own connection, verify, one-click and OAuth starts, sharing, telemetry, diagnose) and nothing else, because it lives in that end user's browser. `app` lets your server act as the whole application (list every connection, manage webhooks, configure SSL and Power, bulk create, purchases). Never hand an `app` token to a browser.

## Response `201`

Token issued

- object
  - `token` string, required: the widget JWT (Bearer)
  - `token_type` string, required
  - `expires_in` integer, required: lifetime in seconds (default 900)
  - `scope` 'connect' | 'app': the scope the token was minted with

## Other responses

- `400`: Error envelope: {code, title, details}.
- `401`: Error envelope: {code, title, details}.
- `429`: `rate_limited`: too many token requests, either from this address or for this `application_id`. Retry shortly.

---

[API](https://skmtc.dev/customdomain/apis/customdomain-control-plane-api.md) · [All operations](https://skmtc.dev/customdomain/apis/customdomain-control-plane-api/llms.txt) · [OpenAPI document](https://skmtc.dev/customdomain/apis/customdomain-control-plane-api/revisions/34d89cb7a7cd?raw)
