HUCK.CHATGOOD COMPANY. BETTER ANSWERS.Built to include everyone

API keys & error handling

Sanctum credentials, scopes, expiration, rotation and safe retries.

Examples should earn your trust. If a step is wrong, incomplete or out of date, use its report link to send the exact section to our team.

Huck has three authentication boundaries: your server uses a scoped service key, an end user uses a short-lived chat session, and your action receiver signs callbacks with its action secret. These credentials are not interchangeable. A console sign-in never substitutes for a service key.

The API base is https://huck.chat/api/v1. Use HTTPS directly. Service keys belong in your backend environment or secret manager, never HTML, browser storage, source control, analytics, or query strings.

Create the right key

Report an issue with this section

Open Developer setup → Create a scoped server key. Use separate keys for ingestion, session issuance, deployment automation, and operational monitoring. A secret is shown once. New tokens use Laravel Sanctum's id|huck_… format; treat the complete value as opaque. Existing service keys issued before the Sanctum migration keep working until their original expiration or revocation.

Integration Required scopes
Send, delete, retry, or batch records knowledge:write
Read records and reconcile their manifest knowledge:read
Synchronize identities and issue chat sessions subjects:read, subjects:write, sessions:create
Revoke a specific chat session from your backend sessions:revoke
Read/test access rules and readable labels access:read
Save access drafts or labels access:write
Publish access changes access:publish
Read/save prompt versions prompts:read, prompts:write
Publish an evaluated prompt prompts:publish
Read results, save goldens, request evaluations evaluations:read, evaluations:write, evaluations:run respectively
Read/create action definitions actions:read, actions:write
Read/save/publish appearance appearance:read, appearance:write, appearance:publish respectively
Manage service keys keys:manage plus every permission being delegated
Monitor integration status operations:read
Export/erase an identity's retained data privacy:export, privacy:delete respectively

The key determines the workspace. Do not include workspace_id or tenant_id in an API request. Every operation requires its own scope; there is no wildcard scope for customer-created keys.

A first request

Report an issue with this section

Set the variables in your server environment. The placeholder below must be replaced with a real server key, not a chat session. The domain guides use HUCK_INGEST_KEY, HUCK_RELEASE_KEY, and HUCK_MANAGEMENT_KEY for separately scoped integrations; map those names to the keys you create. HUCK_API_URL is the common application URL.

export HUCK_API_URL='https://huck.chat'
export HUCK_SERVICE_KEY='YOUR_SERVER_KEY'
curl --fail-with-body "$HUCK_API_URL/api/v1/knowledge/manifest?after_id=0" \
  -H "Authorization: Bearer $HUCK_SERVICE_KEY" \
  -H 'Accept: application/json'

Read requests have no body. Write requests use a JSON object with Content-Type: application/json. Mutation parameters belong in that body, never the URL. Duplicate JSON property names are rejected. The API-wide request limit is 2,000,000 bytes; individual operations have smaller limits.

Laravel server-side example:

// config/services.php
'huck' => [
    'url' => env('HUCK_API_URL', 'https://huck.chat'),
    'key' => env('HUCK_SERVICE_KEY'),
],

// Your application service. Do not return this client or credential to the browser.
$response = Http::baseUrl(rtrim(config('services.huck.url'), '/').'/api/v1/')
    ->withToken(config('services.huck.key'))
    ->withOptions(['allow_redirects' => false])
    ->acceptJson()->connectTimeout(5)->timeout(20)
    ->get('knowledge/manifest', ['after_id' => 0]);
$response->throw();
$manifest = $response->json();

Native Node.js server-side example; no Huck SDK is required:

export async function huck(path, {method = 'GET', body, idempotencyKey} = {}) {
  const headers = {Authorization: `Bearer ${process.env.HUCK_SERVICE_KEY}`,
    Accept: 'application/json'};
  if (body !== undefined) headers['Content-Type'] = 'application/json';
  if (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey;
  const response = await fetch(`${process.env.HUCK_API_URL}/api/v1/${path}`, {
    method, headers, redirect: 'error', signal: AbortSignal.timeout(20000),
    body: body === undefined ? undefined : JSON.stringify(body),
  });
  const payload = response.status === 204 ? null : await response.json();
  if (!response.ok) {
    throw Object.assign(new Error(payload?.detail ?? 'Huck request failed'), {
      status: response.status, requestId: response.headers.get('X-Request-ID'),
      problem: payload, retryAfter: response.headers.get('Retry-After'),
    });
  }
  return payload;
}

The helper intentionally leaves retry decisions to the workflow. A lost response to a key-creation request must not result in blindly creating another key.

Expiration, permanent keys, and rotation

Report an issue with this section

Keys expire after 90 days by default. Explicitly set permanent: true to choose no expiration. An explicit expires_at must be in the future and less than one year away. An expired or revoked key cannot be reactivated; create a replacement.

{
  "name": "Production ingestion",
  "scopes": ["knowledge:read", "knowledge:write"],
  "permanent": false,
  "expires_at": "2027-01-01T00:00:00Z"
}

Send this to POST /keys with an authorized management key. The example date must still be a future date when used. The response is 201 with key metadata and a one-time secret. GET /keys returns paginated metadata, never secrets. The metadata includes id, version, scopes, expires_at, permanent, revoked_at, and last_used_at.

To change an active key's deadline without replacing its secret, send its current version:

curl --fail-with-body -X PUT "$HUCK_API_URL/api/v1/keys/$KEY_ID/lifetime" \
  -H "Authorization: Bearer $HUCK_MANAGEMENT_KEY" \
  -H 'Content-Type: application/json' \
  --data '{"expected_version":1,"permanent":true}'

For a finite deadline, use permanent: false with expires_at. A 409 means the key changed: reload its metadata and ask your operator to review the current state. A management key cannot grant permissions it lacks, make a child permanent when the management key expires, or extend a child beyond its own lifetime.

POST /keys/{id}/rotate with {} immediately revokes the previous token and returns a new secret. For a gradual rollout, create a second key, install and verify it, then call DELETE /keys/{oldId} with {}. Rotation is immediate, not a grace period. To invalidate all current workspace chat sessions at the same time, use {"revoke_sessions":true}; this also requires sessions:revoke. Revoking only a service key does not implicitly revoke sessions it previously minted.

Successful service API responses include X-Huck-Key-Expires-At (ISO timestamp or never) and X-Huck-Key-Status (active, expiring, permanent, expired, or revoked, depending on the final key state). The control panel shows deadlines and upcoming expiration. Email reminders depend on configured delivery and a verified workspace owner; use your own monitoring as well.

Errors you can act on

Report an issue with this section

API errors use application/problem+json. Store the X-Request-ID / request_id when contacting support; do not include bearer secrets or private source contents.

{
  "type": "about:blank",
  "title": "Conflict",
  "status": 409,
  "detail": "The revision is stale or already contains different data.",
  "instance": "/api/v1/knowledge",
  "message": "The revision is stale or already contains different data.",
  "request_id": "example-request-id"
}
Status What to do
400 Fix malformed JSON, duplicate fields, invalid header syntax, or misplaced parameters.
401 Check the correct credential type, expiry/revocation, and chat origin. Chat clients obtain a fresh session through their authenticated backend.
403 Check the scope and access rules. Retrying the same authorization does not grant access.
404 Check the resource ID in this workspace; foreign resources are unavailable.
409 Reconcile the current revision, idempotency receipt, lifecycle state, or evaluation fingerprint. Do not blindly overwrite.
412 A subject ETag changed. Reload authoritative permissions and the current ETag.
413 Split the request into smaller records/batches.
415 Use a supported JSON content type.
422 Correct field errors, missing hierarchy ancestors, unsupported schemas, limits, or quota validation.
428 Read the subject first, then send its ETag in If-Match.
429 Honor Retry-After; reduce concurrency and add jitter.
5xx Retain the request ID. Retry only when the operation's documented idempotency contract makes that safe.

Idempotency is operation-specific. Knowledge accepts a per-record key; evaluation requests and action invocations require their own header. Reuse the same key and identical payload after an uncertain response. Do not assume Idempotency-Key turns every API mutation into an idempotent operation.

Continue with knowledge ingestion, identity and hierarchy access, prompt evaluations, or programmable actions.

A little help.
A lot of possibility.

Your knowledge. Your assistant. Your identity.

Start a free Sandbox