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:
- Poll or subscribe to the run's lifecycle.
- Cancel it, if you change your mind.
- Page through the valid and invalid records once it finishes, or kick off an asynchronous export for a large result set.
- 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.
Credits
What a credit is, why heavier routes cost more, and how estimates, reservations, and budgets keep a run from turning into a surprise.
MCP server
Give an agent the same domain model the dashboard has — under a scoped key, with the same policy, credit, and audit rules, and no long operations hidden inside one opaque call.