Skip to content
Version: Current

Your first request

import StatusPill from ‘../../../components/StatusPill.astro’;

This tutorial takes you from a running gateway to one answered request. It covers three audiences: API users calling from code, CLI users testing from a shell, and operators verifying a deployment.

  • An acumen gateway process answering on 127.0.0.1:4020 (loopback). The operator runbook covers starting it.
  • A running gateway under systemd on the host that serves ai.tcp.systems (see the acumen runbook).
  • A virtual key with the chat scope. Create one with acumen invite on the consumer surface, or POST /v1/router/keys with an admin key.
  • A virtual key in $ACUMEN_KEY.
  • The base URL: http://127.0.0.1:4020 (loopback) or https://ai.tcp.systems (public, verified 2026-10-21).
  • curl on the host.

If you are integrating an SDK or HTTP client, point it at the base URL and send a chat completion.

Terminal window
curl -sS http://127.0.0.1:4020/healthz

200 means the process answers. A 502 means the edge is in front of a gateway that is not running. Fix the gateway before continuing.

Terminal window
curl -sS -H "Authorization: Bearer $ACUMEN_KEY" http://127.0.0.1:4020/v1/models

The response uses the OpenAI list shape. The model values you can pass are the public aliases: auto, cheap, coding, reason, vision, search.

Terminal window
curl -sS http://127.0.0.1:4020/v1/chat/completions \
-H "Authorization: Bearer $ACUMEN_KEY" \
-H 'Content-Type: application/json' \
-d '{"model":"cheap","messages":[{"role":"user","content":"ping"}]}'

A 200 carries an OpenAI chat.completion object. The model field in the reply names the model that actually answered, which is often not the alias you asked for.

4. Read the failure when there is no answer

Section titled “4. Read the failure when there is no answer”

An empty completion is a failure even on HTTP 200. An exhausted candidate list is a 502 with a code from the error reference. The code is the thing to branch on: the prose around it is not stable.

  • The listener answers on 127.0.0.1:4020.
  • A virtual key is accepted as Authorization: Bearer ….
  • model accepts a public alias, and the reply names the real model via X-Acumen-Served-Model (added 2026-11-04).
Symptom Likely cause Next step
401 invalid virtual key Key unknown or revoked Verify key exists in /v1/router/keys or re-create it
403 denied by scope Key lacks chat scope Re-create key with chat scope
403 denied by model Alias not on key’s allowlist Remove allowlist or add the alias
502 all-candidates-failed All eligible providers failed See diagnose a 502

If you are testing from a shell, use the same curl commands. The key difference is managing the key value.

Terminal window
curl -X POST http://127.0.0.1:4020/v1/router/keys \
-H "Authorization: Bearer $ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"cli-test","scopes":["chat"]}'

Save the returned vk_... value. It is shown once.

Terminal window
export ACUMEN_KEY=vk_...

3. Run the requests from the API user section

Section titled “3. Run the requests from the API user section”

The commands are identical. Use $ACUMEN_KEY in the Authorization header.

Symptom Likely cause Next step
401 after key creation Key value not exported or typo Re-export ACUMEN_KEY from the create response
403 scope Forgot chat scope in create call Re-create with "scopes":["chat"]
502 on first try No providers configured yet See add a provider

If you are verifying a deployment, check the gateway health and provider eligibility before sending traffic.

Terminal window
curl -sS http://127.0.0.1:4020/healthz # 200 = process alive
curl -sS http://127.0.0.1:4020/readyz # 200 = store open, at least one eligible seat

readyz returns {"ready":"false","reason":"no eligible seat"} when no provider is configured or all are disabled.

Terminal window
curl -sS -H "Authorization: Bearer $ADMIN_KEY" http://127.0.0.1:4020/v1/router/catalog

This shows each seat: plan, kind, eligibility, credential path (name only), model count, and circuit state. No credential values are printed.

Use the same request as the API user section, with an admin key or a chat-scoped key.

Terminal window
curl -sS -H "Authorization: Bearer $ADMIN_KEY" http://127.0.0.1:4020/v1/router/stats | jq .decisions

The decisions array shows recent routing choices: candidates considered, scores, and why the winner was picked.

Symptom Likely cause Next step
readyz 503 no eligible seat No provider keys configured Set provider env vars and restart, or use acumen invite flow
catalog shows cooling seats Provider keys invalid or quota exhausted Check seat last_error_code; fix key or wait for cooldown
stats shows 0 eligible seats All seats disabled or unauthenticated Review ACUMEN_ALLOWED_HOSTS, key env vars, and catalog output