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.
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 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_usdis a lifetime budget. At the cap the key returns429 budget_exhausteduntil you raise it or mint a new key.rpm_limitandconcurrency_limitare the key's own window, never above your plan. One customer going over does not throttle another.allowed_originslets 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
# 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 -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 -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 -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 "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
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_customer_ref, invalid_total_cap, invalid_rpm_limit, invalid_concurrency_limit, invalid_origins, invalid_grace, nothing_to_change | Fix the body. |
| 403 | admin_scope_required | This key cannot mint. |
| 404 | key_not_found, request_not_found | Not yours, or never existed. |
| 409 | key_revoked, already_rotated | Read the message; already_rotated includes current_key_hash. |
| 429 | child_key_limit, budget_exhausted, rate_limit_exceeded, concurrency_limit_exceeded | Rate 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
- Mint a test key with an explicit lifetime budget and customer reference.
- Use the child key for one inference request; use the parent key to inspect its history.
- Confirm usage is attributed to the expected customer and budget.
- Test rotation and revocation in a non-production integration before relying on them.