Skip to content

Client tokens ​

2 endpoints. Every path below is relative to the API base URL and already includes the /api/v1 prefix — see conventions for authentication, pagination, errors and idempotency.

Endpoint
POST/client-tokensExchange a public key for a short-lived client token bound to an origin, assistant and session
POST/client-tokens/verifyVerify a client token and return its claims. Useful for SDK development and for the gateway

Endpoints ​

Exchange a public key for a short-lived client token bound to an origin, assistant and session ​

POST/client-tokens

The Origin header is checked against the key's allowed origins; a request from an origin the key does not name is refused even with a valid key.

  • No scope is enforced in the handler — any valid key may call it.
  • The Origin header is checked, not a body field. A missing CREDENTIAL_ENCRYPTION_KEY is also surfaced as a 403 authentication_error rather than a 500.

Request body

FieldTypeRequiredNotes
public_keystringyesmax 128 chars
assistant_idstring | null—
user_idstring | null—max 128 chars
session_idstring | null—max 64 chars
ttlinteger | null—min 60. max 3600
bash
curl -X POST "$CALLAGENT_API_URL/client-tokens" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
       "public_key": "string",
       "assistant_id": "01JQ0000000000000000000000",
       "user_id": "01JQ0000000000000000000000",
       "session_id": "01JQ0000000000000000000000",
       "ttl": 1
     }'
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/client-tokens`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "public_key": "string",
    "assistant_id": "01JQ0000000000000000000000",
    "user_id": "01JQ0000000000000000000000",
    "session_id": "01JQ0000000000000000000000",
    "ttl": 1
  }),
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.type}: ${error.message}`);
}

const { data } = await response.json();
php
<?php

$handle = curl_init(getenv('CALLAGENT_API_URL') . '/client-tokens');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
        'Accept: application/json',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
    'public_key' => 'string',
    'assistant_id' => '01JQ0000000000000000000000',
    'user_id' => '01JQ0000000000000000000000',
    'session_id' => '01JQ0000000000000000000000',
    'ttl' => 1,
]),
]);

$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);

if ($status >= 400) {
    throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}

$data = $body['data'];
python
import os, requests

response = requests.post(
    f"{os.environ['CALLAGENT_API_URL']}/client-tokens",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    json={
      "public_key": "string",
      "assistant_id": "01JQ0000000000000000000000",
      "user_id": "01JQ0000000000000000000000",
      "session_id": "01JQ0000000000000000000000",
      "ttl": 1
    },
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
201data-wrapped object

Errors

StatusWhen it happens
403Unknown/inactive public key, disallowed origin or assistant, or inactive organization
422Validation failed on public_key, assistant_id, session_id or ttl

Example response, generated from the schema:

json
{
  "data": {
    "token": "string",
    "token_type": "string",
    "expires_at": "2026-01-01T12:00:00+00:00",
    "expires_in": {},
    "organization_id": null,
    "assistant_id": null,
    "session_id": null
  }
}

Verify a client token and return its claims. Useful for SDK development and for the gateway ​

POST/client-tokens/verify
  • No scope is enforced in the handler — any valid key may call it.
  • An invalid token is answered by the handler itself with 401 plus error.code (wrong_audience, wrong_origin, key_revoked, expired) — not by the global auth middleware.

Request body

FieldTypeRequiredNotes
tokenstringyes
bash
curl -X POST "$CALLAGENT_API_URL/client-tokens/verify" \
  -H "Authorization: Bearer $CALLAGENT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{
       "token": "string"
     }'
js
const response = await fetch(`${process.env.CALLAGENT_API_URL}/client-tokens/verify`, {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.CALLAGENT_API_KEY}`,
    Accept: 'application/json',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "token": "string"
  }),
});

if (!response.ok) {
  const { error } = await response.json();
  throw new Error(`${error.type}: ${error.message}`);
}

const { data } = await response.json();
php
<?php

$handle = curl_init(getenv('CALLAGENT_API_URL') . '/client-tokens/verify');

curl_setopt_array($handle, [
    CURLOPT_CUSTOMREQUEST => 'POST',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => [
        'Authorization: Bearer ' . getenv('CALLAGENT_API_KEY'),
        'Accept: application/json',
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS => json_encode([
    'token' => 'string',
]),
]);

$body = json_decode(curl_exec($handle), true);
$status = curl_getinfo($handle, CURLINFO_HTTP_CODE);
curl_close($handle);

if ($status >= 400) {
    throw new RuntimeException("{$body['error']['type']}: {$body['error']['message']}");
}

$data = $body['data'];
python
import os, requests

response = requests.post(
    f"{os.environ['CALLAGENT_API_URL']}/client-tokens/verify",
    headers={"Authorization": f"Bearer {os.environ['CALLAGENT_API_KEY']}"},
    json={
      "token": "string"
    },
    timeout=30,
)
response.raise_for_status()
data = response.json()["data"]

Responses

StatusBody
200data-wrapped object

Errors

StatusWhen it happens
401
422The token field is missing

Example response, generated from the schema:

json
{
  "data": {
    "valid": true,
    "claims": {}
  }
}

Documented against the code. Anything not yet implemented is labelled, never described as working.