Ray9

Public API

The stable /v1 contract — scoped API keys, idempotency keys on anything retriable, stable error codes with correlation IDs, and pagination.

One model, not a second product

The public API is not a cut-down mirror of the dashboard. Both call the same application services, and both observe the same authorization, idempotency, policy, credit, state-transition, and audit rules.

A Flow created in the builder is operable from the API. A run started by an agent appears in the dashboard, with the same evidence, charged the same way. There is no feature that exists only in the UI because it was too awkward to expose, and no back door that skips a check the UI performs.

The /v1 contract

Public clients — your code, the CLI, an SDK, the MCP server — use the stable, versioned /v1 contract. It is an explicit allowlist of operations with a public OpenAPI document.

The dashboard uses a separate first-party contract. That one is internal, it moves with the UI, and it is not something to build against — a public API key is rejected on it by design. If a capability you need is not on /v1, ask for it rather than reverse-engineering what the dashboard does.

The resources exposed are the ones you would expect from the domain model:

  • organizations
  • templates
  • Flows, and their versions
  • runs
  • datasets, and the records in them
  • monitoring policies, and change events
  • schedules
  • usage
  • webhook operations

Authentication

Requests carry an organization-scoped API key. Keys are scoped by organization, resource, action, and optionally environment, so a key that only needs to read records can be issued to only read records.

Keys use a reserved prefix that makes them unmistakable — including to a secret scanner looking through a repository you accidentally pushed. Ray9 rejects a request that supplies two different kinds of credential rather than picking one and hoping.

Ordinary API clients send the key in the x-api-key header. The MCP server authenticates with the same kind of key. See MCP.

Idempotency

Any operation that creates something or spends credits — creating a run, in particular — requires an explicit idempotency key.

This is not optional politeness. Network calls time out after the server has already committed; a client that retries without an idempotency key is a client that will eventually start two runs, spend twice the credits, and write duplicate records. With one, the retry returns the original result.

Deliveries follow the same principle from the other side: webhook events carry stable, semantic event IDs so your handler can deduplicate them. See Webhooks.

Long operations return immediately

Creating a run does not hold the connection open until the data is ready. It returns a durable run ID straight away.

From there:

  1. Poll or subscribe to the run's lifecycle.
  2. Cancel it, if you change your mind.
  3. Page through the valid and invalid records once it finishes, or kick off an asynchronous export for a large result set.
  4. Inspect the attempts and the structured failure when something did not work.

The run ID is a stable, durable identity. It survives restarts on Ray9's side and page reloads on yours, and it means the same thing in the dashboard, the API, the CLI, and MCP.

Errors

Errors carry a stable machine-readable code and a human-readable message that says what to do about it. The code is part of the contract: you can branch on it, and it will not change meaning underneath you.

The failure categories you will see on a run — policy_denied, target_blocked, auth_required, rate_limited, navigation_failed, extraction_failed, timeout, cancelled, platform_error — are documented in Execution routes. They are the same categories the dashboard shows, because they come from the same place.

Every response carries a correlation ID. Quote it when something is wrong and it can be traced through the whole chain: request, run, attempt, artifact, record, delivery.

Pagination

List endpoints are paginated, and large result sets are meant to be paged rather than pulled in one enormous response. When you want the whole thing, use an export: it runs as a background job and produces a downloadable file rather than a request that times out on both ends.

Versioning

The /v1 contract is versioned, and so are the event schemas. Ray9 is liberal about accepting additive fields, and conservative about changing what an existing one means — a stored definition is never silently reinterpreted.

Beyond the API

The same contract underlies the CLI and the MCP server. They are surfaces over /v1, not separate implementations with their own opinions about what a run is.

On this page