Skip to main content
The Curious API is four JSON endpoints over HTTPS. Every request is a POST with a bearer key; every response is JSON with an X-Request-Id header. The machine-readable contract is the OpenAPI 3.1 document this reference renders from, at /openapi/curious.openapi.yaml.

Base URL

Every path in this reference is relative to it.

Authentication

Keys come in two types and two environments. curious_sk_ keys are secret, live on your servers, and may do everything. curious_pk_ keys are publishable, safe in a browser, serve-only (decide without declaration fields, and events), and accepted only from an origin on the key’s allowlist. Test keys carry a test segment (curious_sk_test_…) and address the same elements with separate users, so test traffic never touches live learning. A missing, malformed, unknown or revoked key is a 401. See Keys and environments.

Requests

  • JSON bodies, Content-Type: application/json. Field names are snake_case.
  • Unknown fields are a 400 on decide, declare and identify, with a message naming the field and its path: Received unknown parameter: control.hypothesi. events ignores unknown fields, except created_at and the retired as_of, which are a 400.
  • decide and events have a batch form on the same path; see Batch calls.

Responses

  • A success is a 200 with the endpoint’s object. Optional fields (warnings, replayed, duplicate) appear only when they apply.
  • Every error is { "error": "<code>", "message": "<text>" }, with request_id added on internal faults. The codes and what to do about each are on Errors.
  • Every response, success or failure, carries X-Request-Id: the id of that request on our side. Log it and quote it to support. Browsers can read it; it is exposed through CORS, as is Retry-After.

Idempotency and rate limits

events dedupes on a caller-supplied id; decide replays a committed decision for a repeated idempotency_key. See Idempotency. Each organization may make 1,000 requests per minute per endpoint; see Rate limits.

Versioning

The version is the /v1 segment of the path, and the OpenAPI document above is the contract for it. A change that would break a correct client of /v1 would arrive as a new path version, not inside /v1.