Skip to content

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:

200 OK
{
  "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 : keepalive comment 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 choices and the call's usage, then data: [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):

text/event-stream
: 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 content is a string, or an array of text and image_url parts.
  • 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
    true for 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_effort sets how long Klok thinks: low, medium, xhigh. Skena Cloud sets medium when 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.

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:

request body
{
  "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,… (or jpeg, webp, gif). Anything else answers 400 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.

request body
{
  "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_tokens at $0.40 per million and usage.completion_tokens at $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_tokens as output ($0.000004 a token). With max_tokens 4,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_tokens and toward max_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

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.

402 Payment Required
{
  "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_tokens to 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
    model is missing or is not "klok-1".
    What to do
    Send "klok-1".
  • Status
    400
    error.code
    invalid-messages
    Meaning
    messages is 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 content is 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 text nor image_url.
    What to do
    Send text and images only.
  • Status
    400
    error.code
    embedded-images-required
    Meaning
    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.
    What to do
    Embed the image as a data URL.
  • Status
    400
    error.code
    invalid-output-limit
    Meaning
    max_tokens is 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_tokens and max_completion_tokens.
    What to do
    Send one of them.
  • Status
    400
    error.code
    one-completion-required
    Meaning
    n is not 1.
    What to do
    Leave n out, or send 1.
  • Status
    400
    error.code
    invalid-stream-mode
    Meaning
    stream is not true or false.
    What to do
    Send a boolean.
  • Status
    400
    error.code
    response-storage-unavailable
    Meaning
    store is not false.
    What to do
    Leave store out, 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.

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-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 on inference-queue-expired and inference-draining.
  • Header
    x-should-retry
    Meaning
    false on every 409: OpenAI's SDKs read it and do not retry what cannot change.
  • Header
    X-Skena-Dispatch
    Meaning
    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_tokens to 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.

200 OK
{
  "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/completions and GET /v1/models (beside Kvick's POST /v1/systemone). There are no embeddings, files, assistants or stored completions: any other path answers 404 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 (n is 1).
  • Klok's reasoning arrives in reasoning_content, beside content (delta.reasoning_content when streaming).
  • Refusals use OpenAI's error envelope with Skena's own codes, and type is skena_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.