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.
| Model | Parameters | Price per 1M input tokens | Notes |
|---|---|---|---|
| cogent-nano | 32M | $0.02 | Fastest Cogent model. 32M parameters. |
| cogent-large | 400M | $0.03 | Most 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
- Create an account and top up your balance in the dashboard (from $1, in SOL or USDC).
- Create an API key under Dashboard → Keys. It is shown once; store it as a secret.
- Export it and make a request:
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"]
}
]
}
JSONThe response:
{
"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.
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:
{
"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."
}
}
]
}| Field | Type | Description |
|---|---|---|
modelrequired | string | A model id from GET /v1/models, e.g. cogent-nano. |
staterequired | string | any JSON | What 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. |
questionsrequired | array | 1 to 64 questions, answered together. |
questions[].key | string | The name of the question in the answer. Optional: q0, q1, … by position. Keys must be unique. |
questions[].typerequired | string | choice, score or noul. See question types. |
questions[].instructionsrequired | string | The question, as text. |
questions[].optionsrequired | string[] | 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[].descriptions | object | Option 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_logits | boolean | Only on /v1/decisions: also return each option's logit. |
Question types
| Type | Meaning | Options |
|---|---|---|
| choice | Pick one of several options. | Any ids, in any order. |
| score | Rate on an ordered scale. | The levels in order, lowest first. The position of a level is part of what the model reads. |
| noul | A 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 examplechoice: What should happen? - a
choiceoption:{id}: {description}, for examplestop: Halt the agent.(stop:with no description) - a
scoreoption:level {r} of {n} {id}: {description}, wherercounts from 1 in the order given - a
nouloption: as achoiceoption, and without a descriptionfalsereadsfalse: No, this does not hold.andtruereadstrue: Yes, this holds.
For the request above, the questions and options read:
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:
{"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.
[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.
| Limit | Value | Error code |
|---|---|---|
| Body size | 512 KiB | request_too_large |
| Questions per request | 1 to 64 | too_many_questions |
| Options per question | 1 to 64 | too_many_options |
| Options per request | 512 | too_many_options |
| Question type | choice, score or noul | invalid_question |
| Question keys | unique within the request | invalid_question |
| Option ids | unique within the question | invalid_question |
Per part, in tokens
Longer parts are truncated on the right; truncated tokens are not read and not billed.
| Part | Limit |
|---|---|
| State | 2,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
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{
"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
}| Field | Type | Description |
|---|---|---|
id | string | The request id, req_ + 24 hex characters. Also in the x-request-id header. |
object | string | decide |
model | string | The model that answered. |
decisions | object | Question key → option id → probability. |
usage.input_tokens | integer | Tokens read and billed. See billing. |
usage.cost_usd | string | The charge, in USD with 9 decimals. |
usage.cost_nanousd | integer | The same charge in nano-USD (1 USD = 10⁹). |
latency_ms | integer | Time spent running the model. |
Scores
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{
"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)
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{
"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"
}
}| Field | Type | Description |
|---|---|---|
id | string | dec_ + the same 24 hex characters as the request id in x-request-id. |
object | string | decision |
created | integer | Unix time, seconds. |
choices[].index | integer | Position of the question in the request. |
choices[].key | string | The question key. |
choices[].type | string | The question type. |
choices[].label | string | The option with the highest probability. |
choices[].probabilities | object | Option id → probability. |
choices[].logits | object | Option id → logit. Only with return_logits: true. |
usage.prompt_tokens | integer | Input tokens billed. total_tokens is the same; completion_tokens is always 0. |
usage.cost_usd | string | The charge, in USD with 9 decimals. |
List models
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{
"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
One entry of the list. Unknown or unavailable ids get 404 model_not_found.
curl https://c1.glim.cx/api/v1/models/cogent-nano{
"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
Liveness, and the models loaded in the serving process.
curl https://c1.glim.cx/api/health{"status": "ok", "models": ["cogent-nano", "cogent-large"]}Response headers
| Header | On | Value |
|---|---|---|
| x-request-id | every response | Request id, req_ + 24 hex characters. Include it when you report a problem. |
| x-cogent-input-tokens | billed responses | Input tokens billed. |
| x-cogent-cost-usd | billed responses | The charge for this request, USD. |
| x-cogent-balance-usd | billed responses | Your balance after this request, USD. |
| x-ratelimit-limit-requests | authenticated /v1 responses | Requests per minute allowed for this key. |
| x-ratelimit-remaining-requests | authenticated /v1 responses | Requests left in the current one-minute window. |
| x-ratelimit-reset-requests | authenticated /v1 responses | Time until the window has room again, e.g. 1.250s. |
| retry-after | 429 and 503 responses | Seconds to wait before retrying. |
Errors
Every error, on every route, has the same shape, compatible with OpenAI's:
{
"error": {
"message": "questions[2].type must be one of choice, score, noul.",
"type": "invalid_request_error",
"param": "questions[2].type",
"code": "invalid_question"
}
}| Field | Type | Description |
|---|---|---|
error.message | string | What went wrong, for people. |
error.type | string | The error class, from the table below. |
error.code | string | null | A stable machine-readable code. |
error.param | string | null | The offending field, when there is one, e.g. questions[2].type. |
| Status | Type | Codes |
|---|---|---|
| 400 | invalid_request_error | invalid_json, missing_field, invalid_question, too_many_questions, too_many_options |
| 401 | authentication_error | missing_api_key, invalid_api_key, account_disabled |
| 402 | insufficient_quota | insufficient_balance |
| 403 | permission_error | account_disabled |
| 404 | not_found_error | model_not_found, not_found |
| 413 | invalid_request_error | request_too_large |
| 429 | rate_limit_error | rate_limit_exceeded |
| 500 | api_error | internal_error |
| 503 | overloaded_error | overloaded, model_unavailable, service_unavailable |
| 504 | api_error | upstream_timeout |
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_nanousd = ceil(input_tokens × price_usd_per_mtok × 1000) # 1 USD = 1,000,000,000 nano-USDFor 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_balanceand 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.
- 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.
- Pay with a browser wallet (Phantom, Solflare, Backpack and other Wallet Standard wallets), or scan the QR code with a mobile wallet.
- 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.