Skip to content

Authentication

Every request to a /v1/* endpoint requires an API key, sent as a bearer token in the Authorization header:

Terminal window
curl -s https://astral-external-api-iksoi6t3nq-ue.a.run.app/v1/western/positions \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"subject": {"datetime": "1990-05-15T14:30:00-04:00"}}'

That is the entire auth model: one header on every request. There is no OAuth client-credentials flow — no token endpoint, no refresh step, no expiring access token to babysit — and no HTTP Basic, which dates from an era of credentials-in-code and offers nothing over a bearer key for server-to-server calls. A static key in a header is the settled answer for machine clients, and it is the only mechanism the API accepts.

The health probes (GET /health, GET /ready) are the exception: they are unauthenticated so load balancers and uptime checks can reach them without a secret.

Keys are prefixed by environment:

Prefix Use
ak_test_ Development and evaluation
ak_live_ Production traffic

The prefix is deliberately identifiable. You can tell at a glance — in a log line, a config diff, or a secret scanner rule — which environment a key belongs to, without ever exposing the rest of the key. If you log anything about a key, log only its prefix.

The API is in early access. Keys are provisioned manually: request one through your early-access contact and you will receive an ak_test_ key (and an ak_live_ key when you are ready for production).

Once you have a key, export it in your shell — the examples across these docs assume $API_KEY is set:

Terminal window
export API_KEY=ak_test_your_key_here

Authentication failures return 401 with an RFC 9457 problem body. Two codes cover the space.

No Authorization header at all yields auth.missing:

Terminal window
curl -s https://astral-external-api-iksoi6t3nq-ue.a.run.app/v1/western/positions \
-H "Content-Type: application/json" \
-d '{"subject": {"datetime": "2000-01-01T00:00:00Z"}}'

A header carrying a key the API does not recognize — revoked, mistyped, or from the wrong environment — yields auth.invalid_key:

Terminal window
curl -s https://astral-external-api-iksoi6t3nq-ue.a.run.app/v1/western/positions \
-H "Authorization: Bearer ak_test_not_a_real_key" \
-H "Content-Type: application/json" \
-d '{"subject": {"datetime": "2000-01-01T00:00:00Z"}}'

Branch on the code field, not the message text. See Errors for the full catalog.

Each account can hold two active keys at once. That is the rotation contract: you never have to revoke a key before its replacement is live, so rotation is zero-downtime by construction.

  1. Request a second key. Both keys now authenticate.
  2. Deploy the new key to your services.
  3. Confirm traffic has moved (no more requests on the old key).
  4. Revoke the old key.

Rotate on your own schedule, and immediately if a key may have leaked. During early access, steps 1 and 4 go through your early-access contact.

  • Environment variables, not source code. Inject keys through your deployment environment or secret manager. A key in a repository is a key you should consider leaked — rotate it.
  • Server-side only. Never ship a key in a browser bundle, a mobile app, or anything else that runs on hardware you don’t control; anyone can extract it. Route those clients through your own backend, which holds the key and calls the API.
  • Don’t log full keys. The ak_test_ / ak_live_ prefix is safe and useful in logs; the remainder is not.

API spec v0.1.0 · docs 0e4f5ec