Skip to content
cogentby glim

API reference

Everything you need to call Cogent over HTTP: the request format, what the model reads, every endpoint, errors, limits and billing.

Overview

Cogent answers typed multi-question decisions. A request holds a state (any text, or any JSON value) and a list of questions about it. Each question has a type, an instruction and a set of options. The response gives a probability for every option of every question; the probabilities of each question sum to 1.

All questions are answered from one encoder pass for requests up to 1,024 tokens. Because no option reads another option or another question, asking a question alone or together with others gives the same answer, up to float rounding.

Models
ModelParametersPrice per 1M input tokensNotes
cogent-nano32M$0.02Fastest Cogent model. 32M parameters.
cogent-large400M$0.03Most accurate Cogent model. 400M parameters.

Base URL: https://c1.glim.cx/api. All bodies are JSON (Content-Type: application/json). The models are open source: see glim-oss/cogent-infer.

Quick start

  1. Create an account and top up your balance in the dashboard (from $1, in SOL or USDC).
  2. Create an API key under Dashboard → Keys. It is shown once; store it as a secret.
  3. Export it and make a request:
Shell
export COGENT_API_KEY="cg-…"
curl https://c1.glim.cx/api/v1/decide \
  -H "Authorization: Bearer $COGENT_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "model": "cogent-nano",
  "state": "Customer: my export to CSV stops at 99% for projects over 10,000 rows. Audit is on Friday.",
  "questions": [
    {
      "key": "queue",
      "type": "choice",
      "instructions": "Which team should handle this ticket?",
      "options": ["billing", "engineering", "account_management", "spam"]
    },
    {
      "key": "urgent",
      "type": "noul",
      "instructions": "This ticket needs attention today.",
      "options": ["false", "true"]
    }
  ]
}
JSON

The response:

200 OK
{
  "id": "req_4be1d0a9c37f2e86b5d1a0c4",
  "object": "decide",
  "model": "cogent-nano",
  "decisions": {
    "queue": {
      "billing": 0.041,
      "engineering": 0.902,
      "account_management": 0.049,
      "spam": 0.008
    },
    "urgent": {"false": 0.236, "true": 0.764}
  },
  "usage": {"input_tokens": 96, "cost_usd": "0.000001920", "cost_nanousd": 1920},
  "latency_ms": 18
}

Response values on this page are illustrative: the shapes and arithmetic are exact, the probabilities, token counts and latencies are examples.

Authentication

Send your API key as a bearer token. x-api-key: <key> is accepted too.

Header
Authorization: Bearer cg-…
  • Keys start with cg- followed by 43 characters. Only a hash is stored: a lost key cannot be shown again, so create a new one and revoke the old one.
  • Revoking a key in the dashboard takes effect within 30 seconds. Requests with a revoked or unknown key get 401 invalid_api_key; without a key, 401 missing_api_key.
  • /v1/* allows cross-origin requests, but an API key in browser code is visible to anyone who opens the page. Call the API from your server.

Request format

The three inference endpoints take the same body. This is examples/case.json from the model repository, with a model id added:

Request body
{
  "model": "cogent-nano",
  "state": {
    "agent": {"autonomy": "supervised", "model": "support-bot"},
    "constraints": ["Never issue refunds above 500 EUR without approval"],
    "task": "Resolve the customer's billing complaint.",
    "trace_summary": {
      "steps": 7,
      "tool_errors": 2,
      "irreversible_actions": 1,
      "constraint_violations": 1,
      "last_action": "issued refund of 850 EUR"
    }
  },
  "questions": [
    {
      "key": "action",
      "type": "choice",
      "instructions": "What should the observability system do with this trace?",
      "options": ["continue", "human_review", "observe", "stop"],
      "descriptions": {
        "continue": "Let the agent proceed without interruption.",
        "human_review": "Queue this trace for a human to review.",
        "observe": "Keep watching without intervening.",
        "stop": "Halt the agent immediately."
      }
    },
    {
      "key": "review",
      "type": "noul",
      "instructions": "This trace requires human review.",
      "options": ["false", "true"]
    },
    {
      "key": "risk",
      "type": "score",
      "instructions": "How risky was the agent's behaviour in this trace?",
      "options": ["0", "1", "2", "3"],
      "descriptions": {
        "0": "Benign: read-only or clearly safe actions.",
        "1": "Low: routine writes within scope.",
        "2": "Elevated: risky or out-of-scope actions.",
        "3": "Severe: damage or a violated constraint."
      }
    }
  ]
}
Request body fields
FieldTypeDescription
modelrequiredstringA model id from GET /v1/models, e.g. cogent-nano.
staterequiredstring | any JSONWhat the questions are about. A string is read as it is. Any other JSON value is serialised with Python's json.dumps defaults, and that text is what the model reads.
questionsrequiredarray1 to 64 questions, answered together.
questions[].keystringThe name of the question in the answer. Optional: q0, q1, … by position. Keys must be unique.
questions[].typerequiredstringchoice, score or noul. See question types.
questions[].instructionsrequiredstringThe question, as text.
questions[].optionsrequiredstring[]1 to 64 option ids, unique within the question. Numbers are accepted and converted to strings (2 becomes "2"); booleans, null, objects and arrays are rejected. At most 512 options per request.
questions[].descriptionsobjectOption id → the meaning of that option. Give them when you have them: an option's id and its description are all the model reads about it.
return_logitsbooleanOnly on /v1/decisions: also return each option's logit.

Question types

Question types
TypeMeaningOptions
choicePick one of several options.Any ids, in any order.
scoreRate on an ordered scale.The levels in order, lowest first. The position of a level is part of what the model reads.
noulA yes/no statement: does it hold?["false", "true"]

Write a noul question as a statement (“This trace requires human review.”), not as a question. Its answer is the probability that the statement holds, in true.

What the model reads, exactly

The text of a request is built like this, and nothing else is added:

  • a question: {type}: {instructions}, for example choice: What should happen?
  • a choice option: {id}: {description}, for example stop: Halt the agent. (stop: with no description)
  • a score option: level {r} of {n} {id}: {description}, where r counts from 1 in the order given
  • a noul option: as a choice option, and without a description false reads false: No, this does not hold. and true reads true: Yes, this holds.

For the request above, the questions and options read:

Rendered questions
choice: What should the observability system do with this trace?
    continue: Let the agent proceed without interruption.
    human_review: Queue this trace for a human to review.
    observe: Keep watching without intervening.
    stop: Halt the agent immediately.

noul: This trace requires human review.
    false: No, this does not hold.
    true: Yes, this holds.

score: How risky was the agent's behaviour in this trace?
    level 1 of 4 0: Benign: read-only or clearly safe actions.
    level 2 of 4 1: Low: routine writes within scope.
    level 3 of 4 2: Elevated: risky or out-of-scope actions.
    level 4 of 4 3: Severe: damage or a violated constraint.

and the state, a JSON object, reads:

Rendered state
{"agent": {"autonomy": "supervised", "model": "support-bot"}, "constraints": ["Never issue refunds above 500 EUR without approval"], "task": "Resolve the customer's billing complaint.", "trace_summary": {"steps": 7, "tool_errors": 2, "irreversible_actions": 1, "constraint_violations": 1, "last_action": "issued refund of 850 EUR"}}

One request is one token sequence. The state reads only the state; a question reads the state and itself; an option reads the state, its own question and itself. Positions follow the virtual pair “state, question, option”, so every question starts right after the state and every option right after its own question.

Layout
[CLS] state [SEP] | question 1 [SEP] | option 1.1 [SEP] | option 1.2 [SEP] | question 2 [SEP] | option 2.1 [SEP] | ...

Limits

Per request

Requests outside these limits are rejected with 400 (or 413 for size) before billing.

Request limits
LimitValueError code
Body size512 KiBrequest_too_large
Questions per request1 to 64too_many_questions
Options per question1 to 64too_many_options
Options per request512too_many_options
Question typechoice, score or noulinvalid_question
Question keysunique within the requestinvalid_question
Option idsunique within the questioninvalid_question

Per part, in tokens

Longer parts are truncated on the right; truncated tokens are not read and not billed.

Token limits
PartLimit
State2,048, [CLS] and [SEP] included
Question (type and instructions)512, plus [SEP]
Option (id and description)128, plus [SEP]

A request of more than 1,024 tokens is split into several encoder sequences that each hold the whole state and some of the options. Since no option reads another, the split changes nothing but float rounding, and you are billed once per token. The reader head sees at most 256 state vectors; a longer state is mean-pooled in strides.

Decide

POST https://c1.glim.cx/api/v1/decideAPI key

Probabilities for every option of every question. Options keep your order; each question's probabilities sum to 1.

curl https://c1.glim.cx/api/v1/decide \
  -H "Authorization: Bearer $COGENT_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "model": "cogent-nano",
  "state": {
    "agent": {"autonomy": "supervised", "model": "support-bot"},
    "constraints": ["Never issue refunds above 500 EUR without approval"],
    "task": "Resolve the customer's billing complaint.",
    "trace_summary": {
      "steps": 7,
      "tool_errors": 2,
      "irreversible_actions": 1,
      "constraint_violations": 1,
      "last_action": "issued refund of 850 EUR"
    }
  },
  "questions": [
    {
      "key": "action",
      "type": "choice",
      "instructions": "What should the observability system do with this trace?",
      "options": ["continue", "human_review", "observe", "stop"],
      "descriptions": {
        "continue": "Let the agent proceed without interruption.",
        "human_review": "Queue this trace for a human to review.",
        "observe": "Keep watching without intervening.",
        "stop": "Halt the agent immediately."
      }
    },
    {
      "key": "review",
      "type": "noul",
      "instructions": "This trace requires human review.",
      "options": ["false", "true"]
    },
    {
      "key": "risk",
      "type": "score",
      "instructions": "How risky was the agent's behaviour in this trace?",
      "options": ["0", "1", "2", "3"],
      "descriptions": {
        "0": "Benign: read-only or clearly safe actions.",
        "1": "Low: routine writes within scope.",
        "2": "Elevated: risky or out-of-scope actions.",
        "3": "Severe: damage or a violated constraint."
      }
    }
  ]
}
JSON
200 OK
{
  "id": "req_8f3a2c71d04b9e6a5c13f7d2",
  "object": "decide",
  "model": "cogent-nano",
  "decisions": {
    "action": {
      "continue": 0.018,
      "human_review": 0.297,
      "observe": 0.071,
      "stop": 0.614
    },
    "review": {"false": 0.087, "true": 0.913},
    "risk": {"0": 0.011, "1": 0.046, "2": 0.214, "3": 0.729}
  },
  "usage": {"input_tokens": 312, "cost_usd": "0.000006240", "cost_nanousd": 6240},
  "latency_ms": 41
}
Decide response fields
FieldTypeDescription
idstringThe request id, req_ + 24 hex characters. Also in the x-request-id header.
objectstringdecide
modelstringThe model that answered.
decisionsobjectQuestion key → option id → probability.
usage.input_tokensintegerTokens read and billed. See billing.
usage.cost_usdstringThe charge, in USD with 9 decimals.
usage.cost_nanousdintegerThe same charge in nano-USD (1 USD = 10⁹).
latency_msintegerTime spent running the model.

Scores

POST https://c1.glim.cx/api/v1/scoresAPI key

The logits behind the probabilities, before the softmax over each question's options. An option's logit depends only on the state, its question and its own text. Same body and billing as decide.

curl https://c1.glim.cx/api/v1/scores \
  -H "Authorization: Bearer $COGENT_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "model": "cogent-nano",
  "state": {
    "agent": {"autonomy": "supervised", "model": "support-bot"},
    "constraints": ["Never issue refunds above 500 EUR without approval"],
    "task": "Resolve the customer's billing complaint.",
    "trace_summary": {
      "steps": 7,
      "tool_errors": 2,
      "irreversible_actions": 1,
      "constraint_violations": 1,
      "last_action": "issued refund of 850 EUR"
    }
  },
  "questions": [
    {
      "key": "action",
      "type": "choice",
      "instructions": "What should the observability system do with this trace?",
      "options": ["continue", "human_review", "observe", "stop"],
      "descriptions": {
        "continue": "Let the agent proceed without interruption.",
        "human_review": "Queue this trace for a human to review.",
        "observe": "Keep watching without intervening.",
        "stop": "Halt the agent immediately."
      }
    },
    {
      "key": "review",
      "type": "noul",
      "instructions": "This trace requires human review.",
      "options": ["false", "true"]
    },
    {
      "key": "risk",
      "type": "score",
      "instructions": "How risky was the agent's behaviour in this trace?",
      "options": ["0", "1", "2", "3"],
      "descriptions": {
        "0": "Benign: read-only or clearly safe actions.",
        "1": "Low: routine writes within scope.",
        "2": "Elevated: risky or out-of-scope actions.",
        "3": "Severe: damage or a violated constraint."
      }
    }
  ]
}
JSON
200 OK
{
  "id": "req_8f3a2c71d04b9e6a5c13f7d2",
  "object": "scores",
  "model": "cogent-nano",
  "scores": {
    "action": {
      "continue": -1.9263,
      "human_review": 0.877,
      "observe": -0.554,
      "stop": 1.6033
    },
    "review": {"false": -1.1754, "true": 1.1754},
    "risk": {"0": -2.1482, "1": -0.7174, "2": 0.8199, "3": 2.0456}
  },
  "usage": {"input_tokens": 312, "cost_usd": "0.000006240", "cost_nanousd": 6240},
  "latency_ms": 40
}

The response is the same as for decide, with "object": "scores" and scores (question key → option id → logit) instead of decisions.

Decisions (OpenAI-style)

POST https://c1.glim.cx/api/v1/decisionsAPI key

The same answer in an envelope shaped like OpenAI's APIs: a list of choices, one per question, each with its label (the most probable option), and usage with prompt_tokens. Set return_logits: true to get the logits too, from the same model run at no extra cost.

curl https://c1.glim.cx/api/v1/decisions \
  -H "Authorization: Bearer $COGENT_API_KEY" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "model": "cogent-nano",
  "state": {
    "agent": {"autonomy": "supervised", "model": "support-bot"},
    "constraints": ["Never issue refunds above 500 EUR without approval"],
    "task": "Resolve the customer's billing complaint.",
    "trace_summary": {
      "steps": 7,
      "tool_errors": 2,
      "irreversible_actions": 1,
      "constraint_violations": 1,
      "last_action": "issued refund of 850 EUR"
    }
  },
  "questions": [
    {
      "key": "action",
      "type": "choice",
      "instructions": "What should the observability system do with this trace?",
      "options": ["continue", "human_review", "observe", "stop"],
      "descriptions": {
        "continue": "Let the agent proceed without interruption.",
        "human_review": "Queue this trace for a human to review.",
        "observe": "Keep watching without intervening.",
        "stop": "Halt the agent immediately."
      }
    },
    {
      "key": "review",
      "type": "noul",
      "instructions": "This trace requires human review.",
      "options": ["false", "true"]
    },
    {
      "key": "risk",
      "type": "score",
      "instructions": "How risky was the agent's behaviour in this trace?",
      "options": ["0", "1", "2", "3"],
      "descriptions": {
        "0": "Benign: read-only or clearly safe actions.",
        "1": "Low: routine writes within scope.",
        "2": "Elevated: risky or out-of-scope actions.",
        "3": "Severe: damage or a violated constraint."
      }
    }
  ],
  "return_logits": true
}
JSON
200 OK
{
  "id": "dec_8f3a2c71d04b9e6a5c13f7d2",
  "object": "decision",
  "created": 1762950000,
  "model": "cogent-nano",
  "choices": [
    {
      "index": 0,
      "key": "action",
      "type": "choice",
      "label": "stop",
      "probabilities": {
        "continue": 0.018,
        "human_review": 0.297,
        "observe": 0.071,
        "stop": 0.614
      },
      "logits": {
        "continue": -1.9263,
        "human_review": 0.877,
        "observe": -0.554,
        "stop": 1.6033
      }
    },
    {
      "index": 1,
      "key": "review",
      "type": "noul",
      "label": "true",
      "probabilities": {"false": 0.087, "true": 0.913},
      "logits": {"false": -1.1754, "true": 1.1754}
    },
    {
      "index": 2,
      "key": "risk",
      "type": "score",
      "label": "3",
      "probabilities": {"0": 0.011, "1": 0.046, "2": 0.214, "3": 0.729},
      "logits": {"0": -2.1482, "1": -0.7174, "2": 0.8199, "3": 2.0456}
    }
  ],
  "usage": {
    "prompt_tokens": 312,
    "completion_tokens": 0,
    "total_tokens": 312,
    "cost_usd": "0.000006240"
  }
}
Decisions response fields
FieldTypeDescription
idstringdec_ + the same 24 hex characters as the request id in x-request-id.
objectstringdecision
createdintegerUnix time, seconds.
choices[].indexintegerPosition of the question in the request.
choices[].keystringThe question key.
choices[].typestringThe question type.
choices[].labelstringThe option with the highest probability.
choices[].probabilitiesobjectOption id → probability.
choices[].logitsobjectOption id → logit. Only with return_logits: true.
usage.prompt_tokensintegerInput tokens billed. total_tokens is the same; completion_tokens is always 0.
usage.cost_usdstringThe charge, in USD with 9 decimals.

List models

GET https://c1.glim.cx/api/v1/modelsAPI key optional

The models you can call right now, with their prices and limits, in OpenAI's list shape.

curl https://c1.glim.cx/api/v1/models
200 OK
{
  "object": "list",
  "data": [
    {
      "id": "cogent-nano",
      "object": "model",
      "created": 1762905600,
      "owned_by": "glim",
      "display_name": "Cogent Nano",
      "description": "Fastest Cogent model. 32M parameters.",
      "params": "32M",
      "pricing": {"input_usd_per_mtok": "0.02"},
      "limits": {
        "max_state_tokens": 2048,
        "max_question_tokens": 512,
        "max_option_tokens": 128,
        "max_questions": 64,
        "max_options_per_question": 64,
        "max_options_total": 512
      }
    },
    {
      "id": "cogent-large",
      "object": "model",
      "created": 1762905600,
      "owned_by": "glim",
      "display_name": "Cogent Large",
      "description": "Most accurate Cogent model. 400M parameters.",
      "params": "400M",
      "pricing": {"input_usd_per_mtok": "0.03"},
      "limits": {
        "max_state_tokens": 2048,
        "max_question_tokens": 512,
        "max_option_tokens": 128,
        "max_questions": 64,
        "max_options_per_question": 64,
        "max_options_total": 512
      }
    }
  ]
}

Retrieve a model

GET https://c1.glim.cx/api/v1/models/{id}API key optional

One entry of the list. Unknown or unavailable ids get 404 model_not_found.

curl https://c1.glim.cx/api/v1/models/cogent-nano
200 OK
{
  "id": "cogent-nano",
  "object": "model",
  "created": 1762905600,
  "owned_by": "glim",
  "display_name": "Cogent Nano",
  "description": "Fastest Cogent model. 32M parameters.",
  "params": "32M",
  "pricing": {"input_usd_per_mtok": "0.02"},
  "limits": {
    "max_state_tokens": 2048,
    "max_question_tokens": 512,
    "max_option_tokens": 128,
    "max_questions": 64,
    "max_options_per_question": 64,
    "max_options_total": 512
  }
}

Health

GET https://c1.glim.cx/api/healthNo auth

Liveness, and the models loaded in the serving process.

curl https://c1.glim.cx/api/health
200 OK
{"status": "ok", "models": ["cogent-nano", "cogent-large"]}

Response headers

Response headers
HeaderOnValue
x-request-idevery responseRequest id, req_ + 24 hex characters. Include it when you report a problem.
x-cogent-input-tokensbilled responsesInput tokens billed.
x-cogent-cost-usdbilled responsesThe charge for this request, USD.
x-cogent-balance-usdbilled responsesYour balance after this request, USD.
x-ratelimit-limit-requestsauthenticated /v1 responsesRequests per minute allowed for this key.
x-ratelimit-remaining-requestsauthenticated /v1 responsesRequests left in the current one-minute window.
x-ratelimit-reset-requestsauthenticated /v1 responsesTime until the window has room again, e.g. 1.250s.
retry-after429 and 503 responsesSeconds to wait before retrying.

Errors

Every error, on every route, has the same shape, compatible with OpenAI's:

400 Bad Request
{
  "error": {
    "message": "questions[2].type must be one of choice, score, noul.",
    "type": "invalid_request_error",
    "param": "questions[2].type",
    "code": "invalid_question"
  }
}
Error fields
FieldTypeDescription
error.messagestringWhat went wrong, for people.
error.typestringThe error class, from the table below.
error.codestring | nullA stable machine-readable code.
error.paramstring | nullThe offending field, when there is one, e.g. questions[2].type.
Error types and codes
StatusTypeCodes
400invalid_request_errorinvalid_json, missing_field, invalid_question, too_many_questions, too_many_options
401authentication_errormissing_api_key, invalid_api_key, account_disabled
402insufficient_quotainsufficient_balance
403permission_erroraccount_disabled
404not_found_errormodel_not_found, not_found
413invalid_request_errorrequest_too_large
429rate_limit_errorrate_limit_exceeded
500api_errorinternal_error
503overloaded_erroroverloaded, model_unavailable, service_unavailable
504api_errorupstream_timeout
Retry 429, 500 and 503 with exponential backoff. Do not retry other 4xx errors unchanged. Requests that fail with 4xx, 500 or 503 are not charged; a charged request whose model run fails, or that runs past the server's deadline, is refunded. A 504 upstream_timeout comes from the proxy and is not retry-safe: the request may still have completed and been billed, so check your usage before retrying it.

Rate limits

Each API key may make 600 requests per minute by default, counted over a sliding one-minute window. Above it the API answers 429 rate_limit_exceeded with a retry-after header; wait that long and retry. The limit is set per key, so you can spread independent workloads over several keys.

Separately, when the inference queue is full the API answers 503 overloaded. Nothing is charged; retry after a short delay.

Billing and tokens

Requests are paid from your prepaid balance, per input token. Input tokens are the tokens the model reads after truncation: the state, every question and every option, including the [CLS] and [SEP] tokens of the layout. There are no output tokens.

Cost of one request
cost_nanousd = ceil(input_tokens × price_usd_per_mtok × 1000)        # 1 USD = 1,000,000,000 nano-USD

For example, 312 input tokens on cogent-nano at $0.02 per 1M cost 6240 nano-USD, which the API reports as "cost_usd": "0.000006240" ($0.00000624).

  • The balance is checked and charged before the model runs. If it is too low the API answers 402 insufficient_balance and nothing runs.
  • If the model fails after the charge, the charge is refunded.
  • A request split into several encoder sequences is billed once per token.
  • Money in responses is a string with up to 9 decimals of USD. Parse it as a decimal, not a float, if you add it up; or use the integer cost_nanousd.
  • Requests made from the dashboard playground are billed the same way.

Prices: cogent-nano $0.02, cogent-large $0.03 per 1M input tokens. See pricing for a calculator.

Top-ups with SOL and USDC

Your balance is in USD and is topped up with SOL or USDC on Solana mainnet, using Solana Pay transfer requests. Payments go directly to our wallet; there is no payment processor in between.

  1. In Dashboard → Billing, choose SOL or USDC and an amount from $1 to $10,000. You get a quote: the exact token amount to send.
  2. Pay with a browser wallet (Phantom, Solflare, Backpack and other Wallet Standard wallets), or scan the QR code with a mobile wallet.
  3. Your balance is credited once the transaction is finalized on Solana, usually within a minute. The dashboard shows the status live.
  • USDC is credited 1:1 in USD.
  • SOL is priced when you create the quote, and that price holds for 30 minutes. A SOL payment that lands after the quote expires is credited at the price at that time.
  • What arrives is what is credited: an underpayment is credited in proportion. SOL sent above the quoted amount is credited at the lower of the quoted price and the price when it arrives.
  • You pay the Solana network fee (about 0.000005 SOL). The full amount you send is credited; no fee is taken from it.
Pay from a self-custody wallet, and pay each quote once. Each quote carries a unique reference that the payment transaction must include; withdrawals from exchanges cannot include it and are not detected. A second payment to a quote that is already confirmed is not credited automatically.