klok api
Klok API
Chat with Skena Klok, Skena AI's reasoning model, through an OpenAI-compatible API: text, images and tool calls, streamed or whole. Point an OpenAI SDK at https://api.skena.ai/v1 and send your Skena API key.
- Base URL
- https://api.skena.ai
- OpenAI SDKs
base_url = https://api.skena.ai/v1- Model
- klok-1
- Auth
Authorization: Bearer skl_…, a key you make on the dashboard's API key page (one per app or server; keys)- Paid from
- Skena AI credit: $0.40 per million input tokens, $4.00 per million output tokens
Quickstart
Put your key in SKENA_API_KEY and ask one question:
curl https://api.skena.ai/v1/chat/completions \
-H "Authorization: Bearer $SKENA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "klok-1",
"messages": [
{
"role": "user",
"content": "In one sentence, why is the sky blue?"
}
],
"max_tokens": 4000
}'The answer, in OpenAI's shape. reasoning_content is how Klok thought; content is its answer:
{
"id": "chatcmpl-…",
"object": "chat.completion",
"created": 1793750400,
"model": "klok-1",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"reasoning_content": "The user wants one sentence. Rayleigh scattering…",
"content": "Air molecules scatter short blue wavelengths of sunlight far more than long red ones, so blue light reaches your eyes from every part of the sky."
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 22,
"completion_tokens": 318,
"total_tokens": 340
}
}Streaming
Send stream: true for server-sent events, in OpenAI's chat.completion.chunk shape. Stream anything long:
stream = client.chat.completions.create(
model="klok-1",
messages=[{"role": "user", "content": "How does a French press work?"}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.usage: # the last piece: what the call used
print("\n", chunk.usage)- Once Klok has the call, Skena Cloud writes a
: keepalivecomment every 15 seconds until the answer starts, so a long prompt does not end the connection. SSE readers, the OpenAI SDKs' included, skip lines that start with:. - The last piece has empty
choicesand the call'susage, thendata: [DONE]. It always comes: Skena Cloud asks for it. - Cloudflare, in front of Skena Cloud, closes a call that has not started answering within 100 seconds. A streamed call starts answering as soon as Klok does; a call without streaming answers only when it is done, so a long answer can be cut off. Stream it.
- Close the stream to stop Klok. The call is charged for what had run by then.
A stream, trimmed to the fields that matter (each piece also carries OpenAI's id, object, created and model):
: keepalive
data: {"choices":[{"index":0,"delta":{"reasoning_content":"The user"}}]}
…
data: {"choices":[{"index":0,"delta":{"content":" Enjoy."}}]}
data: {"choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: {"choices":[],"usage":{"prompt_tokens":28,"completion_tokens":904,"total_tokens":932}}
data: [DONE]The request
POST/v1/chat/completions
One request carries a conversation and the options for its answer. Skena Cloud accepts exactly these fields and refuses any other with 400 unsupported-request-field, so a mistyped option never goes quietly unused:
- Field
- modelrequired
- Type
- string
- Description
- Send "klok-1". The answer names the model you sent.
- Field
- messagesrequired
- Type
- array
- Description
- 1 to 4,096 messages in OpenAI's chat format. A message's
contentis a string, or an array oftextandimage_urlparts.
- Field
- max_tokensmax_completion_tokens
- Type
- integer
- Description
- The most tokens Klok may write, its reasoning included: 1 to 32,000, and 32,000 when you send neither. Send one of the two, not both. It sets the call's hold.
- Field
- stream
- Type
- boolean
- Description
truefor server-sent events. Recommended for long answers.
- Field
- stream_options
- Type
- object
- Description
- Accepted. When you stream, Skena Cloud sets it so the call's usage always arrives as the last piece.
- Field
- toolstool_choiceparallel_tool_calls
- Type
- OpenAI's tool calling
- Description
- Passed to Klok as you send them.
- Field
- chat_template_kwargs
- Type
- object
- Description
- Arguments for Klok's chat template.
reasoning_effortsets how long Klok thinks:low,medium,xhigh. Skena Cloud setsmediumwhen you name no effort.
- Field
- reasoning_effort
- Type
- string
- Description
- Accepted and passed on as sent. When it is present, Skena Cloud adds no effort of its own. Klok's template reads the effort from
chat_template_kwargs, which is where Skena's own apps set it.
- Field
- temperaturetop_ptop_kmin_ppresence_penaltyfrequency_penaltyrepetition_penaltyrepeat_penaltyseedstop
- Type
- as in OpenAI's API
- Description
- Sampling, passed to Klok as you send it.
- Field
- response_formatlogprobstop_logprobs
- Type
- as in OpenAI's API
- Description
- Passed to Klok as you send them.
- Field
- n
- Type
- integer
- Description
- Only
1: one answer per call.
- Field
- store
- Type
- boolean
- Description
- Only
false: there is no stored-completions API.
| Field | Type | Description |
|---|---|---|
| modelrequired | string | Send "klok-1". The answer names the model you sent. |
| messagesrequired | array | 1 to 4,096 messages in OpenAI's chat format. A message's content is a string, or an array of text and image_url parts. |
| max_tokensmax_completion_tokens | integer | The most tokens Klok may write, its reasoning included: 1 to 32,000, and 32,000 when you send neither. Send one of the two, not both. It sets the call's hold. |
| stream | boolean | true for server-sent events. Recommended for long answers. |
| stream_options | object | Accepted. When you stream, Skena Cloud sets it so the call's usage always arrives as the last piece. |
| toolstool_choiceparallel_tool_calls | OpenAI's tool calling | Passed to Klok as you send them. |
| chat_template_kwargs | object | Arguments for Klok's chat template. reasoning_effort sets how long Klok thinks: low, medium, xhigh. Skena Cloud sets medium when you name no effort. |
| reasoning_effort | string | Accepted and passed on as sent. When it is present, Skena Cloud adds no effort of its own. Klok's template reads the effort from chat_template_kwargs, which is where Skena's own apps set it. |
| temperaturetop_ptop_kmin_ppresence_penaltyfrequency_penaltyrepetition_penaltyrepeat_penaltyseedstop | as in OpenAI's API | Sampling, passed to Klok as you send it. |
| response_formatlogprobstop_logprobs | as in OpenAI's API | Passed to Klok as you send them. |
| n | integer | Only 1: one answer per call. |
| store | boolean | Only false: there is no stored-completions API. |
Messages and images
messages is OpenAI's chat format: system, user, assistant (with tool_calls) and tool messages, 1 to 4,096 of them, prompt and answer together within 262,144 tokens. A message's content is a string, or an array of text and image_url parts.
An image goes in an image_url part as a base64 data URL. Links are refused, never fetched:
{
"model": "klok-1",
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "What is the total on this receipt?"
},
{
"type": "image_url",
"image_url": {
"url": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQ..."
}
}
]
}
],
"max_tokens": 2000
}- PNG, JPEG, WebP or GIF, as
data:image/png;base64,…(orjpeg,webp,gif). Anything else answers400 embedded-images-required. - Images and text share the 32 MiB body, and the tokens an image becomes count as input.
Tools
Tool calling is OpenAI's: send tools (and tool_choice, parallel_tool_calls if you like), and Klok answers with tool_calls when it wants one run. Send each result back as a tool message with its tool_call_id. Skena Cloud passes tools to Klok as you send them.
{
"model": "klok-1",
"messages": [
{
"role": "user",
"content": "What is the weather in Stockholm?"
}
],
"tools": [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "The current weather in a city.",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string"
}
},
"required": [
"city"
]
}
}
}
],
"max_tokens": 2000
}Reasoning
Klok reasons before it answers. The reasoning arrives in message.reasoning_content (delta.reasoning_content when streaming), apart from content. It is output: it counts in completion_tokens and toward max_tokens, so leave room for it.
Set how long Klok thinks with chat_template_kwargs.reasoning_effort: low, medium, xhigh. When you name no effort, Skena Cloud sends medium. With the OpenAI Python SDK, pass it in extra_body:
response = client.chat.completions.create(
model="klok-1",
messages=[{"role": "user", "content": "Plan a three-day trip to Gotland."}],
max_tokens=4000,
extra_body={"chat_template_kwargs": {"reasoning_effort": "low"}},
)What a call costs
What a call costs, and what is charged when it stops early:
- Credit pays for
usage.prompt_tokensat $0.40 per million andusage.completion_tokensat $4.00 per million. Cached prompt tokens are charged in full. A call that reads 10,000 tokens and writes 2,000 costs $0.004 + $0.008 = $0.012. - Before a call runs, Skena Cloud sets a hold aside: the whole 262,144-token context as input ($0.105) plus
max_tokensas output ($0.000004 a token). Withmax_tokens4,000 the hold is $0.121; with the default 32,000 it is $0.233. Your available credit must cover it, and only what the call used is charged. - Klok's reasoning is output: it counts in
completion_tokensand towardmax_tokens. - A refusal from Skena Cloud (an
error.code) costs nothing, and so does a request Klok's server refuses before it runs (400 inference-request-rejected). - A call that ends before any of the answer reached you costs nothing, whatever ended it: Klok answering an error, the link to it breaking, an answer that could not be read, or you leaving first. Its hold comes back at once.
- Close a stream mid-answer and the call is charged for what Klok had reported running by then: the prompt it read and the tokens it wrote.
- An answer that reached you without a count of what it used, streamed or whole, is charged its full hold.
Calls at a time
Every call is paid from credit. What the account holds decides how many calls it runs at once, and its place in line when Klok is busy:
- The account holds
- Credit from Skena, and no Skena AI plan
- Calls at a time
- As many as Klok runs at once for everyone
- Place in line
- First
- The account holds
- A Skena AI plan, whatever credit pays
- Calls at a time
- The plan's number
- Place in line
- The plan's place: Ultra, then Max, then Pro
- The account holds
- Only the welcome gift, once a computer has activated it
- Calls at a time
- 1
- Place in line
- Last
| The account holds | Calls at a time | Place in line |
|---|---|---|
| Credit from Skena, and no Skena AI plan | As many as Klok runs at once for everyone | First |
| A Skena AI plan, whatever credit pays | The plan's number | The plan's place: Ultra, then Max, then Pro |
| Only the welcome gift, once a computer has activated it | 1 | Last |
When Klok is busy, a call waits its turn, up to 45 seconds, then answers 503 inference-queue-expired with Retry-After. You send nothing extra: Skena Cloud holds the call's place for you, and its turn comes in the order above, accounts taking turns at each level.
Limits
- Context: 262,144 tokens, prompt and answer together.
max_tokens: 1 to 32,000, and 32,000 when you send none. It includes Klok's reasoning.- Messages: 1 to 4,096 per request.
- The request body: at most 32 MiB, with its length declared in Content-Length (no chunked uploads), all of it within 30 seconds. Every common HTTP client and both OpenAI SDKs declare the length of a JSON body.
- An answer without streaming: at most 16 MiB, and done within Cloudflare's 100 seconds.
- Waiting: up to 45 seconds for a turn, at most 8 of your calls waiting at once and 120 queued a minute.
- Calls paid from credit, Klok and Kvick together: up to 120 a minute, and on the welcome gift 5,000 in 24 hours.
Errors
Skena Cloud's refusals use OpenAI's error envelope, so an OpenAI SDK raises them as its own errors with the status and the body. code is stable; message repeats it.
{
"error": {
"message": "ai-credit-insufficient",
"type": "skena_inference_error",
"code": "ai-credit-insufficient"
}
}- Status
- 401
- error.code
invalid-inference-key- Meaning
- No key, a malformed key, a revoked key, or a device key from Skena Builder or Skena Servant (the API takes Skena API keys only).
- What to do
- Use a key from your dashboard's API key page, or make a new one there.
- Status
- 403
- error.code
account-unverified- Meaning
- The account's email is not verified.
- What to do
- Verify your email.
- Status
- 403
- error.code
account-suspended- Meaning
- The account is suspended.
- What to do
- Contact us.
- Status
- 403
- error.code
ai-credit-required- Meaning
- The account has no Skena AI credit. The API is paid from credit alone, whatever plan the account has.
- What to do
- Check your balance on the dashboard's Skena AI page.
- Status
- 402, 403
- error.code
ai-credit-unavailable- Meaning
- Nothing is left to pay for the call: the welcome gift is used up or expired, and the account has no other credit (403 while the welcome gift is paused).
- What to do
- Check your balance on the dashboard's Skena AI page.
- Status
- 403
- error.code
ai-credit-activation-required- Meaning
- Only the welcome gift is left, and no computer has activated it yet. Refused before the call waits or uploads, so it costs nothing.
- What to do
- Sign in to Skena Builder or Skena Servant on a computer once, and connect Skena AI.
- Status
- 402
- error.code
ai-credit-insufficient- Meaning
- Your available credit does not cover the call's hold.
- What to do
- Lower
max_tokensto shrink the hold, or check your balance on the dashboard's Skena AI page.
- Status
- 400
- error.code
invalid-json- Meaning
- The body is not a JSON object.
- What to do
- Send a JSON object.
- Status
- 400
- error.code
unsupported-request-field- Meaning
- The body has a field Skena Cloud does not accept.
- What to do
- Send only the fields in the table above.
- Status
- 400
- error.code
unknown-model- Meaning
modelis missing or is not "klok-1".- What to do
- Send "klok-1".
- Status
- 400
- error.code
invalid-messages- Meaning
messagesis missing, empty, over 4,096, or holds something that is not an object.- What to do
- Fix
messages.
- Status
- 400
- error.code
invalid-content- Meaning
- A message's
contentis neither a string nor an array of parts. - What to do
- Send a string or an array of parts.
- Status
- 400
- error.code
unsupported-content-type- Meaning
- A content part is neither
textnorimage_url. - What to do
- Send text and images only.
- Status
- 400
- error.code
embedded-images-required- Meaning
- An
image_urlis not a base64 data URL of a PNG, JPEG, WebP or GIF image (data:image/png|jpeg|webp|gif;base64,). Links are refused, never fetched. - What to do
- Embed the image as a data URL.
- Status
- 400
- error.code
invalid-output-limit- Meaning
max_tokensis not a whole number from 1 to 32,000.- What to do
- Send a smaller
max_tokens.
- Status
- 400
- error.code
ambiguous-output-limit- Meaning
- The body has both
max_tokensandmax_completion_tokens. - What to do
- Send one of them.
- Status
- 400
- error.code
one-completion-required- Meaning
nis not 1.- What to do
- Leave
nout, or send 1.
- Status
- 400
- error.code
invalid-stream-mode- Meaning
streamis nottrueorfalse.- What to do
- Send a boolean.
- Status
- 400
- error.code
response-storage-unavailable- Meaning
storeis notfalse.- What to do
- Leave
storeout, or send false.
- Status
- 400
- error.code
invalid-credit-request-id- Meaning
- The Idempotency-Key header is longer than 128 bytes, or holds a line break or a NUL.
- What to do
- Send a shorter key.
- Status
- 400
- error.code
inference-request-rejected- Meaning
- Klok's server refused the request as sent, for example a prompt longer than the context. Nothing is charged.
- What to do
- Shorten the conversation or fix the request.
- Status
- 408
- error.code
inference-upload-timeout- Meaning
- The body did not arrive within 30 seconds.
- What to do
- Send it again, or a smaller one.
- Status
- 409
- error.code
inference-request-already-recorded- Meaning
- The call reused an Idempotency-Key this account already sent, whatever happened to that first call. Sent with
x-should-retry: false. - What to do
- Nothing ran or was charged again. The 409 does not repeat the first answer: to retry a call that failed, send a new key.
- Status
- 411
- error.code
inference-length-required- Meaning
- The body did not declare its length (a chunked upload), or it is empty.
- What to do
- Send the JSON body with a Content-Length header.
- Status
- 413
- error.code
inference-request-too-large- Meaning
- The body is over 32 MiB.
- What to do
- Send smaller or fewer images, or a shorter conversation.
- Status
- 413
- error.code
inference-request-exceeds-memory-budget- Meaning
- The body is too large for Skena Cloud to hold while it waits.
- What to do
- Send a smaller body.
- Status
- 429
- error.code
inference-attempt-limit- Meaning
- Calls paid from credit, Klok and Kvick together: over 120 in the last minute (on the welcome gift, also 5,000 in the last 24 hours). The error carries
resetsAt. - What to do
- Retry after the Retry-After seconds.
- Status
- 429
- error.code
inference-queue-full- Meaning
- More than 8 of your calls are waiting, you queued 120 in the last minute, or Skena Cloud's queue is full.
- What to do
- Wait for a call to finish, then retry (Retry-After).
- Status
- 429
- error.code
inference-concurrency-limit- Meaning
- The account already runs as many calls as it may at once (see Calls at a time).
- What to do
- Wait for one to finish (Retry-After).
- Status
- 429
- error.code
inference-admission-rate-limit- Meaning
- Skena Cloud is taking in too many calls at once, from this account or from everyone.
- What to do
- Retry after the Retry-After seconds.
- Status
- 429
- error.code
inference-memory-limit- Meaning
- Skena Cloud has no room to hold the call right now.
- What to do
- Retry after the Retry-After seconds.
- Status
- 503
- error.code
inference-queue-expired- Meaning
- The call waited 45 seconds for its turn and Klok was still busy.
- What to do
- Retry after the Retry-After seconds (5).
- Status
- 503
- error.code
inference-draining- Meaning
- Skena Cloud is not taking new calls for a moment, for maintenance.
- What to do
- Retry after the Retry-After seconds (5).
- Status
- 503, 409
- error.code
inference-ticket-config-changed- Meaning
- Klok's configuration changed while the call waited or uploaded (409 if it was uploading). Nothing ran.
- What to do
- Send the call again.
- Status
- 503
- error.code
inference-backend-unavailable- Meaning
- Klok could not be reached, or failed.
- What to do
- Retry after a short delay.
- Status
- 503
- error.code
inference-response-incomplete- Meaning
- Klok's answer broke off, or was over 16 MiB without streaming.
- What to do
- Retry; stream long answers.
- Status
- 503
- error.code
inference-response-invalid- Meaning
- Klok's answer could not be read.
- What to do
- Retry after a short delay.
- Status
- 503
- error.code
inference-stream-invalid- Meaning
- Klok did not answer with a stream.
- What to do
- Retry after a short delay.
- Status
- 503
- error.code
inference-cache-isolation-unavailable- Meaning
- A temporary problem on our side.
- What to do
- Retry after a short delay.
- Status
- 503
- error.code
inference-pricing-unavailable- Meaning
- A temporary problem on our side.
- What to do
- Retry after a short delay.
- Status
- 503
- error.code
inference-account-store-unavailable- Meaning
- A temporary problem on our side.
- What to do
- Retry after a short delay.
| Status | error.code | Meaning | What to do |
|---|---|---|---|
| 401 | invalid-inference-key | No key, a malformed key, a revoked key, or a device key from Skena Builder or Skena Servant (the API takes Skena API keys only). | Use a key from your dashboard's API key page, or make a new one there. |
| 403 | account-unverified | The account's email is not verified. | Verify your email. |
| 403 | account-suspended | The account is suspended. | Contact us. |
| 403 | ai-credit-required | The account has no Skena AI credit. The API is paid from credit alone, whatever plan the account has. | Check your balance on the dashboard's Skena AI page. |
| 402, 403 | ai-credit-unavailable | Nothing is left to pay for the call: the welcome gift is used up or expired, and the account has no other credit (403 while the welcome gift is paused). | Check your balance on the dashboard's Skena AI page. |
| 403 | ai-credit-activation-required | Only the welcome gift is left, and no computer has activated it yet. Refused before the call waits or uploads, so it costs nothing. | Sign in to Skena Builder or Skena Servant on a computer once, and connect Skena AI. |
| 402 | ai-credit-insufficient | Your available credit does not cover the call's hold. | Lower max_tokens to shrink the hold, or check your balance on the dashboard's Skena AI page. |
| 400 | invalid-json | The body is not a JSON object. | Send a JSON object. |
| 400 | unsupported-request-field | The body has a field Skena Cloud does not accept. | Send only the fields in the table above. |
| 400 | unknown-model | model is missing or is not "klok-1". | Send "klok-1". |
| 400 | invalid-messages | messages is missing, empty, over 4,096, or holds something that is not an object. | Fix messages. |
| 400 | invalid-content | A message's content is neither a string nor an array of parts. | Send a string or an array of parts. |
| 400 | unsupported-content-type | A content part is neither text nor image_url. | Send text and images only. |
| 400 | embedded-images-required | An image_url is not a base64 data URL of a PNG, JPEG, WebP or GIF image (data:image/png|jpeg|webp|gif;base64,). Links are refused, never fetched. | Embed the image as a data URL. |
| 400 | invalid-output-limit | max_tokens is not a whole number from 1 to 32,000. | Send a smaller max_tokens. |
| 400 | ambiguous-output-limit | The body has both max_tokens and max_completion_tokens. | Send one of them. |
| 400 | one-completion-required | n is not 1. | Leave n out, or send 1. |
| 400 | invalid-stream-mode | stream is not true or false. | Send a boolean. |
| 400 | response-storage-unavailable | store is not false. | Leave store out, or send false. |
| 400 | invalid-credit-request-id | The Idempotency-Key header is longer than 128 bytes, or holds a line break or a NUL. | Send a shorter key. |
| 400 | inference-request-rejected | Klok's server refused the request as sent, for example a prompt longer than the context. Nothing is charged. | Shorten the conversation or fix the request. |
| 408 | inference-upload-timeout | The body did not arrive within 30 seconds. | Send it again, or a smaller one. |
| 409 | inference-request-already-recorded | The call reused an Idempotency-Key this account already sent, whatever happened to that first call. Sent with x-should-retry: false. | Nothing ran or was charged again. The 409 does not repeat the first answer: to retry a call that failed, send a new key. |
| 411 | inference-length-required | The body did not declare its length (a chunked upload), or it is empty. | Send the JSON body with a Content-Length header. |
| 413 | inference-request-too-large | The body is over 32 MiB. | Send smaller or fewer images, or a shorter conversation. |
| 413 | inference-request-exceeds-memory-budget | The body is too large for Skena Cloud to hold while it waits. | Send a smaller body. |
| 429 | inference-attempt-limit | Calls paid from credit, Klok and Kvick together: over 120 in the last minute (on the welcome gift, also 5,000 in the last 24 hours). The error carries resetsAt. | Retry after the Retry-After seconds. |
| 429 | inference-queue-full | More than 8 of your calls are waiting, you queued 120 in the last minute, or Skena Cloud's queue is full. | Wait for a call to finish, then retry (Retry-After). |
| 429 | inference-concurrency-limit | The account already runs as many calls as it may at once (see Calls at a time). | Wait for one to finish (Retry-After). |
| 429 | inference-admission-rate-limit | Skena Cloud is taking in too many calls at once, from this account or from everyone. | Retry after the Retry-After seconds. |
| 429 | inference-memory-limit | Skena Cloud has no room to hold the call right now. | Retry after the Retry-After seconds. |
| 503 | inference-queue-expired | The call waited 45 seconds for its turn and Klok was still busy. | Retry after the Retry-After seconds (5). |
| 503 | inference-draining | Skena Cloud is not taking new calls for a moment, for maintenance. | Retry after the Retry-After seconds (5). |
| 503, 409 | inference-ticket-config-changed | Klok's configuration changed while the call waited or uploaded (409 if it was uploading). Nothing ran. | Send the call again. |
| 503 | inference-backend-unavailable | Klok could not be reached, or failed. | Retry after a short delay. |
| 503 | inference-response-incomplete | Klok's answer broke off, or was over 16 MiB without streaming. | Retry; stream long answers. |
| 503 | inference-response-invalid | Klok's answer could not be read. | Retry after a short delay. |
| 503 | inference-stream-invalid | Klok did not answer with a stream. | Retry after a short delay. |
| 503 | inference-cache-isolation-unavailable | A temporary problem on our side. | Retry after a short delay. |
| 503 | inference-pricing-unavailable | A temporary problem on our side. | Retry after a short delay. |
| 503 | inference-account-store-unavailable | A temporary problem on our side. | Retry after a short delay. |
Headers
Besides Authorization and Content-Type, a request may send:
- Header
Idempotency-Key- Meaning
- Optional: up to 128 bytes, a new one for each call. A call that reuses a key answers
409 inference-request-already-recordedand is neither run nor charged, so a retry can never charge a call twice. The 409 does not repeat the first answer.
| Header | Meaning |
|---|---|
Idempotency-Key | Optional: up to 128 bytes, a new one for each call. A call that reuses a key answers 409 inference-request-already-recorded and is neither run nor charged, so a retry can never charge a call twice. The 409 does not repeat the first answer. |
Response headers worth reading:
- Header
X-Skena-Usage-Id- Meaning
- The call's entry in your credit history, once Skena Cloud has set its hold aside.
- Header
Retry-After- Meaning
- Seconds to wait before retrying: on every 429 (30, or until the limit resets for
inference-attempt-limit), and 5 oninference-queue-expiredandinference-draining.
- Header
x-should-retry- Meaning
falseon every 409: OpenAI's SDKs read it and do not retry what cannot change.
- Header
X-Skena-Dispatch- Meaning
- When present:
not-startedmeans the call never reached Klok, so sending it again cannot run it twice;startedmeans it did.
| Header | Meaning |
|---|---|
X-Skena-Usage-Id | The call's entry in your credit history, once Skena Cloud has set its hold aside. |
Retry-After | Seconds to wait before retrying: on every 429 (30, or until the limit resets for inference-attempt-limit), and 5 on inference-queue-expired and inference-draining. |
x-should-retry | false on every 409: OpenAI's SDKs read it and do not retry what cannot change. |
X-Skena-Dispatch | When present: not-started means the call never reached Klok, so sending it again cannot run it twice; started means it did. |
Calling it well
- Stream anything long. A stream starts answering as soon as Klok does and stays open while it thinks; an answer without streaming must be done within the 100 seconds Cloudflare allows, waiting included.
- Set
max_tokensto what you need: it sets the hold, so a smaller one lets a small balance run more calls at once. - Make one key per app or server, and keep keys on your server: anyone with one can spend your account's credit, on Kvick as well as Klok. Revoke a key that leaks, and the others keep working.
- Retry 429 and 503 after the Retry-After seconds; OpenAI's SDKs do this by themselves. Send each retry with a new Idempotency-Key.
Models
GET/v1/models
Lists every model your key can call, in one answer with two shapes: OpenAI's data (what OpenAI's SDKs read) and the Jev API's models (what Jev clients read, Kvick only). A model is listed only while Skena Cloud serves it.
{
"object": "list",
"data": [
{
"id": "klok-1",
"object": "model",
"owned_by": "skena",
"name": "Skena Klok",
"context_window": 262144
},
{
"id": "kvick-1",
"object": "model",
"owned_by": "skena",
"name": "Skena Kvick"
}
],
"models": [
{
"name": "kvick-1",
"description": "Skena Kvick: calibrated System One decisions (Jev-compatible)."
}
]
}It needs a valid key on an account with credit, and never wakes a model. Its refusals use the skena_inference_error envelope, for Kvick too.
OpenAI compatibility
Code written for OpenAI's chat API works by setting its base URL to https://api.skena.ai/v1, with your key and "model": "klok-1". Where it differs:
- Only
POST /v1/chat/completionsandGET /v1/models(beside Kvick'sPOST /v1/systemone). There are no embeddings, files, assistants or stored completions: any other path answers404 unknown-endpoint. - Fields outside the table above are refused with
400 unsupported-request-field, never ignored. - Images go in as base64 data URLs; links are refused. One answer per call (
nis 1). - Klok's reasoning arrives in
reasoning_content, besidecontent(delta.reasoning_contentwhen streaming). - Refusals use OpenAI's
errorenvelope with Skena's own codes, andtypeisskena_inference_error.
OpenAPI
The same reference, machine-readable, for code generators, API clients and coding agents:
https://skena.ai/docs/klok/openapi.json
- There is no uptime guarantee yet, and the API may change.