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

TypeFieldsAnswer
choiceinstruction, options: 2 or more names, or names mapped to descriptions or nullone of the options, with each option’s probability
booleaninstruction, optional yes and no descriptionstrue or false, with the probability of true
scoreinstruction, min and max, or levels: 2–32 descriptions scored from 0the 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

ModelReadsLimits
crossroads-8bText or JSON; the default48,000 characters of state
crossroads-mText or JSON, with one image or one recording8,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.

StatusMeaningThen
400Invalid input or idempotency keyFix the request
401Missing, invalid, revoked, or expired API keyCheck the key
402Usage billing is not activeActivate billing in the console
409
request_in_progress
The first attempt with this key is still runningRetry the same key a few seconds later
409
previous_request_failed
The first attempt failed and was not billedSend it again with a new key
409
idempotency_conflict
The key was used for different inputUse a new key
413Body exceeds 1.5 MiB, or a question exceeds the model’s tokensSend less
415Content-Type is not application/jsonFix the header
429Account rate or concurrency limit reached, or every GPU stayed busyRetry the same key after Retry-After
502The model backend returned an errorRetry the same key after a short backoff
503The model returned invalid answers, a required service is unavailable, or the request is still runningRetry 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.