Documentation
Create an API key, activate usage billing, and send your first request. Crossroads answers typed questions about a piece of context; it does not write text, hold a conversation or stand in for the model behind a coding agent.
Using a coding agent? Ask it to set up Crossroads with smallmodelslab.ai/agents.md.
Make a request
Replace the host with your Crossroads deployment and keep your API key on the server. Use a unique idempotency key for each distinct request.
curl "$CROSSROADS_URL/v1/decisions" \
-H "Authorization: Bearer $CROSSROADS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: ticket-123-attempt-1" \
-d '{
"state": "Hi! We were charged twice for our team's plan this month. Could you help us sort this out?",
"questions": {
"route": {
"type": "choice",
"instruction": "Which team should handle this request?",
"options": [
"billing",
"engineering",
"sales"
]
},
"needs_reply": {
"type": "boolean",
"instruction": "Does this message need a reply?"
},
"urgency": {
"type": "score",
"instruction": "How urgent is this request?",
"min": 1,
"max": 5
}
}
}'Read the response
Every answer carries the model’s probabilities: a boolean the probability of true, a choice one per option, a score one per level.
{
"id": "5f1c…",
"model": "crossroads-8b",
"answers": {
"route": {
"choice": "billing",
"probabilities": {
"billing": 0.91,
"engineering": 0.06,
"sales": 0.03
}
},
"needs_reply": {
"value": true,
"probability": 0.94
},
"urgency": {
"score": 3.48,
"probabilities": {
"1": 0.02,
"2": 0.08,
"3": 0.41,
"4": 0.38,
"5": 0.11
}
}
},
"usage": {
"input_tokens": 96,
"output_tokens": 0
}
}Question types
| Type | Fields | Answer |
|---|---|---|
| choice | instruction, options: 2 or more names, or names mapped to descriptions or null | one of the options, with each option’s probability |
| boolean | instruction, optional yes and no descriptions | true or false, with the probability of true |
| score | instruction, min and max, or levels: 2–32 descriptions scored from 0 | the expected score in its range, with each level’s probability |
Filter a list
POST /v1/filter takes up to 256 JSON items, each at most 48,000 characters as JSON or 16,000 on Crossroads-M, and an instruction of up to 1,800 characters. It returns the items whose probability of matching is at least cutoff (0.5 by default), highest first, with their probabilities and their positions in your list. model, image and audio work as for decisions: every item is judged against the image or recording. Reusing the idempotency key with only a new cutoff re-ranks the saved probabilities without another charge. A long list runs in batches billed one by one, so a new key after a failed filter bills its finished batches again.
curl "$CROSSROADS_URL/v1/filter" \
-H "Authorization: Bearer $CROSSROADS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: billing-filter-1" \
-d '{
"items": [
{
"title": "Fix invoices"
},
{
"title": "Update landing page"
}
],
"instruction": "Keep billing work",
"cutoff": 0.8
}'Models
| Model | Reads | Limits |
|---|---|---|
crossroads-8b | Text or JSON; the default | 48,000 characters of state |
crossroads-m | Text or JSON, with one image or one recording | 8,192 tokens per question, counting the image or recording; images up to 4,194,304 pixels |
Crossroads 8B costs $0.040 and Crossroads M $0.080 USD per million input tokens; output is free. Each question is scored against the whole state, so a request’s input tokens are, for every question, the state, its instruction and its options, with any image or recording. Asking sixteen questions in one request costs what sixteen requests would, but takes one round trip and counts once against your limits.
A request with a recording is answered by the audio-trained model, built for spoken commands, keywords, spoken language, speech versus music or noise, and spoken intent; emotion, environmental sound and music analysis are not supported.
Write the state
The state is what every question is asked about: a message, a document, or a JSON object or array of related records. Prefer an object whose names say what each part is, and use those names in the instructions. Keep everything a question has to compare, such as a request and the policy it is judged by, in one state. Leave out what no question needs: it costs tokens on every question and can mislead the model.
{
"message": "I was charged twice for order A-104. Please refund the duplicate.",
"order": {
"id": "A-104",
"charges": [
{ "amount_usd": 49, "status": "captured" },
{ "amount_usd": 49, "status": "captured" }
]
},
"refund_policy": "Duplicate charges are refunded in full."
}Act on the probabilities
The probabilities say how sure the model is. Act on an answer when its probability is high, ask for a confirmation or a review in between, and hand the case to a person or another system when it is low. Set each bar by what a wrong answer costs: issuing a refund deserves a higher one than tagging a ticket.
const { choice, probabilities } = answers.route;
if (probabilities[choice] >= 0.9) assign(choice);
else if (probabilities[choice] >= 0.6) suggest(choice);
else sendToPerson();A choice whose probabilities are close together, or a score spread over several levels, usually means the options overlap, the levels are ambiguous or the state lacks what they need; rewrite them before lowering a bar. Typed output constrains the format, not the judgment, and the probabilities are not guaranteed to be calibrated: choose your bars from your own labelled examples.
Patterns
Route a request
Ask one choice over the places a request can go, with an option for none of them. Send clear cases to plain code, and the rest to a person or a larger model.
Score in parts
Break a broad judgment, such as how promising a lead is, into narrow scores with described levels, and combine them with weights you set in code, where you can read and change them.
Guard a language model
Before a message reaches the model, and before its reply reaches the user, ask booleans such as whether it asks for something off limits or contains personal data, then pass, review or block it by your bars.
Narrow retrieved passages
Send the passages a search returned to /v1/filter with the user’s question as the instruction, and give only the ones it keeps to the model that writes the answer. Try other cutoffs with the same idempotency key at no charge.
Limits
Send UTF-8 JSON with Content-Type: application/json; unknown fields are rejected. A request holds 1–16 named questions and up to 48,000 characters of state, as text or as JSON counted in its compact form, and is at most 1.5 MiB. Crossroads-M also accepts one image (JPEG, PNG or WebP) or one recording (WAV or FLAC, at most 30 seconds), each at most 1 MiB as padded base64; with a recording, the state may be empty. By default each account may send 120 requests per minute and four at a time.
Errors and retries
Every error includes a code, a descriptive message, and a request ID; keep the ID when reporting a failure. The same input with the same idempotency key returns the saved response without another charge, so retrying with the same key is always safe.
| Status | Meaning | Then |
|---|---|---|
| 400 | Invalid input or idempotency key | Fix the request |
| 401 | Missing, invalid, revoked, or expired API key | Check the key |
| 402 | Usage billing is not active | Activate billing in the console |
409request_in_progress | The first attempt with this key is still running | Retry the same key a few seconds later |
409previous_request_failed | The first attempt failed and was not billed | Send it again with a new key |
409idempotency_conflict | The key was used for different input | Use a new key |
| 413 | Body exceeds 1.5 MiB, or a question exceeds the model’s tokens | Send less |
| 415 | Content-Type is not application/json | Fix the header |
| 429 | Account rate or concurrency limit reached, or every GPU stayed busy | Retry the same key after Retry-After |
| 502 | The model backend returned an error | Retry the same key after a short backoff |
| 503 | The model returned invalid answers, a required service is unavailable, or the request is still running | Retry the same key after a short backoff |
Data and billing
Crossroads stores the request hash, returned answers, and token usage for retries and billing. It does not store the input state or question instructions. The privacy policy describes what else is kept and who processes it.
Only successfully validated and saved responses enter the billing ledger, together with their usage.