Account

Child keys

Mint a bounded, revocable key for each of your own customers. They call the gateway directly, from a server or a browser; you never proxy their traffic. Each key carries its own budget, rate limit and history, and everything bills into your prepaid balance.

Before minting a key

Keep the parent account key on your server. The management calls below require that account key, not the child credential. Start with a small test budget and store the returned key securely.

Minting is enabled per account. If your account key returns 403 admin_scope_required, write to hashim@straitly.ai.
Mint
POST https://api.straitly.ai/v1/admin/keys
List
GET https://api.straitly.ai/v1/admin/keys
Read one
GET https://api.straitly.ai/v1/admin/keys/{key_hash}
Change controls
PATCH https://api.straitly.ai/v1/admin/keys/{key_hash}
Rotate
POST https://api.straitly.ai/v1/admin/keys/{key_hash}/rotate
Revoke
DELETE https://api.straitly.ai/v1/admin/keys/{key_hash}
Request history
GET https://api.straitly.ai/v1/admin/requests
One request
GET https://api.straitly.ai/v1/admin/requests/{request_id}
Auth
Authorization: Bearer sk-… (your account key, on every call)

The same keys appear in the console under Customer keys, grouped by customer, with spend, rotate and revoke.

Three ids

  • key_ref (lk_…): the stable id of a customer's key. Survives rotation. Use it as your primary key.
  • key_hash: the id of the current credential. Changes on rotation. Goes in management URLs.
  • customer_ref: your own customer id, up to 128 characters. Stored as-is, never interpreted. Filter keys and history by it.

Mint

Every field is optional. The raw key is returned once and never stored.

curl
curl https://api.straitly.ai/v1/admin/keys \
  -H "Authorization: Bearer $STRAITLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "acme-prod",
    "customer_ref": "cust_01H9",
    "total_cap_usd": 500,
    "daily_cap_usd": 50,
    "rpm_limit": 600,
    "concurrency_limit": 64,
    "allowed_origins": ["https://app.acme.com"]
  }'

# 201
# {
#   "key": "sk-...",                    <- shown exactly once
#   "key_ref": "lk_3f9a1c22b0d94e17",   <- stable, survives rotation
#   "key_hash": "0a1c6d9b...",          <- current credential
#   "customer_ref": "cust_01H9",
#   "total_cap_usd": 500, "daily_cap_usd": 50,
#   "rpm_limit": 600, "concurrency_limit": 64,
#   "disabled": false
# }
  • total_cap_usd is a lifetime budget. At the cap the key returns 429 budget_exhausted until you raise it or mint a new key.
  • rpm_limit and concurrency_limit are the key's own window, never above your plan. One customer going over does not throttle another.
  • allowed_origins lets pages on those origins call the inference endpoints directly; the gateway answers the preflight. Keys without it are server-only. CORS is not a secret-keeping mechanism: a key shipped to a browser can be extracted, so use bounded child keys rather than a parent account key.
  • A child key cannot mint keys.

List and read

curl
# newest first, keyset pagination, limit <= 200
curl "https://api.straitly.ai/v1/admin/keys?customer_ref=cust_01H9&include_disabled=true&limit=100" \
  -H "Authorization: Bearer $STRAITLY_API_KEY"
# {"keys": [...], "next_cursor": "..." | null}

curl https://api.straitly.ai/v1/admin/keys/KEY_HASH \
  -H "Authorization: Bearer $STRAITLY_API_KEY"
# key object + "spent_usd", "remaining_usd"

Filters: customer_ref, key_ref, include_disabled (default false).

Change controls

curl
curl -X PATCH https://api.straitly.ai/v1/admin/keys/KEY_HASH \
  -H "Authorization: Bearer $STRAITLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"daily_cap_usd": null, "rpm_limit": 120, "label": "acme-prod-eu"}'

Any of label, customer_ref, total_cap_usd, daily_cap_usd, rpm_limit, concurrency_limit, allowed_origins. null clears a cap or resets a limit to your plan. Live everywhere within 15 seconds. A revoked key cannot be patched (409 key_revoked).

Rotate

curl
curl -X POST https://api.straitly.ai/v1/admin/keys/KEY_HASH/rotate \
  -H "Authorization: Bearer $STRAITLY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"grace_seconds": 3600}'
# 201: new key object with "key" (shown once), same key_ref, "previous_key_hash"

The new credential copies every control and keeps the same key_ref, so caps, spend and history continue as one series. The old credential keeps working for grace_seconds (0 to 7 days), then returns 401. Rotating a credential that was already rotated returns 409 already_rotated with current_key_hash.

Revoke

curl
curl -X DELETE https://api.straitly.ai/v1/admin/keys/KEY_HASH \
  -H "Authorization: Bearer $STRAITLY_API_KEY"
# {"key_hash": "...", "key_ref": "lk_...", "revoked": true}

Immediate and permanent. History stays queryable by key_ref and customer_ref.

Request history

Every response carries x-straitly-request-id. Send x-partner-request-ref: <your id> on a request and it is stored beside it.

curl
curl "https://api.straitly.ai/v1/admin/requests?customer_ref=cust_01H9&since=1789800000&limit=200" \
  -H "Authorization: Bearer $STRAITLY_API_KEY"
# {"requests": [...], "next_cursor": "..." | null}

curl https://api.straitly.ai/v1/admin/requests/REQUEST_ID \
  -H "Authorization: Bearer $STRAITLY_API_KEY"
# or by your own reference:
curl "https://api.straitly.ai/v1/admin/requests/YOUR_REF?by=partner_ref" \
  -H "Authorization: Bearer $STRAITLY_API_KEY"

Filters: key_ref, key_hash, customer_ref, partner_ref, status (pending | completed | failed), since / until (unix seconds or ms). Newest first.

Each row: request_id, partner_ref, key_ref, customer_ref, model, status, final, stream, started_at, ended_at, duration_ms, time_to_first_token_ms, finish_reason, error, usage (input, cache read, cache write, output, total tokens), charge_usd.

A row is written as pending when the request is accepted and updated once to final about two seconds after it ends. A client disconnect mid-stream is failed with error.code = client_disconnect; the usage on that row is what was generated, and what you are charged. Rows already final never change. Retention is 400 days.

Errors

StatusCodeMeaning
400invalid_customer_ref, invalid_total_cap, invalid_rpm_limit, invalid_concurrency_limit, invalid_origins, invalid_grace, nothing_to_changeFix the body.
403admin_scope_requiredThis key cannot mint.
404key_not_found, request_not_foundNot yours, or never existed.
409key_revoked, already_rotatedRead the message; already_rotated includes current_key_hash.
429child_key_limit, budget_exhausted, rate_limit_exceeded, concurrency_limit_exceededRate and concurrency limits may be retried after Retry-After. Key-count and lifetime-budget limits require a control change, not a retry loop.

Verify before handing out access

  1. Mint a test key with an explicit lifetime budget and customer reference.
  2. Use the child key for one inference request; use the parent key to inspect its history.
  3. Confirm usage is attributed to the expected customer and budget.
  4. Test rotation and revocation in a non-production integration before relying on them.

Key safety and partner setup support.