Signup comes with $1 in credit. No subscription needed.Claim your $1
Docs: Run
API reference

Run

POST /v1/run: execute a capability with routing and failover, or a raw endpoint under max_cost.

POST /v1/run

Execute a capability. The maximum billable amount is reserved before the provider call, then settled to the actual charge on success or released on failure. The request waits up to wait_seconds (default 30, max 120) for the result and answers 200 with the finished run; a run still executing answers 202 with the same payload (done: false) to poll at GET /v1/runs/{run_id}?wait=60. Send "async": true to get the 202 immediately. A run that would breach the organization's daily spend guard ends as blocked with error budget_exceeded before any money moves.

Headers

Header Required Notes
Authorization yes Bearer rg_test_... or Bearer rg_live_...
Idempotency-Key recommended any unique string; makes the call safe to retry

Request

{
  "capability": "contact.find",
  "input": {"first_name": "Alex", "last_name": "Rivera", "company_domain": "example.com"},
  "routing": {"provider": "auto", "strategy": "best_value", "max_cost": "0.10", "allow_fallback": true},
  "response": {"include_raw": false}
}
Field Type Default Notes
capability string one of from discover
provider + endpoint strings one of a raw endpoint instead of a capability; path fills {placeholders}, query adds query parameters
input object required must match the capability's input_schema from inspect; for a raw endpoint, the provider's own request body. email.send takes attachments. An HTML entity in a text value (AT&T, as agents sometimes write it) is decoded before a capability runs, and the run stores the decoded input; html and content fields and raw endpoint bodies are sent as given
routing.provider string "auto" auto, or a provider slug to pin; pinning disables cross-provider fallback even with allow_fallback: true
routing.strategy string "best_value" cheapest, fastest, highest_success_rate, best_value
routing.max_cost string none USD cap; offers above it are excluded before the call, fallbacks included, so a cap at the cheapest provider's price also disables failover (a failed run says which fallbacks it excluded). On a raw endpoint it also bounds the hold and the charge: set it on every raw call
routing.allow_fallback bool true only applies with provider: "auto"; set false to disable waterfall failover
routing.fresh bool false run even when this exact input missed in the last 12 hours (see repeated misses)
response.include_raw bool false include the provider's raw payload alongside the normalized result

Attachments on email.send

An email sent from one of your inboxes can carry files: a deck for an investor, a PDF quote, a one-pager. Pass them in input.attachments, on a new message or a reply (in_reply_to):

{
  "capability": "email.send",
  "input": {
    "inbox_id": "alex@getacme.com",
    "to": ["investor@fund.example"],
    "subject": "Acme deck, as promised",
    "text": "Hi Sam,\n\nThe deck is attached.\n\nAlex",
    "attachments": [
      {"filename": "acme-deck.pdf", "content_type": "application/pdf", "url": "https://files.getacme.com/acme-deck.pdf"},
      {"filename": "one-pager.pdf", "data_base64": "JVBERi0xLjQK..."}
    ]
  },
  "routing": {"max_cost": "0.01"}
}
Field Notes
filename required; the plain file name the recipient sees, no path
content_type media type such as application/pdf; guessed from the filename when omitted
url a public https URL the sending provider downloads itself; pre-signed and redirecting URLs work. Exactly one of url or data_base64
data_base64 the file's bytes as standard base64, inline in the request
size_bytes optional with url: the file's size, counted toward the total

Limits per message: 10 files, 10 MB in total, and 4 MB of inline data_base64 (the request body that carries it is capped at 8 MB). Anything larger goes by url. The run's result lists the attachments the provider accepted; the run's stored input keeps each inline file's name, type and size but not its bytes. A send is priced per email whatever it carries. gmail.send does not take attachments yet.

Response

{
  "run_id": "run_...",
  "status": "succeeded",
  "kind": "capability",
  "capability": "contact.find",
  "endpoint": null,
  "provider": "...",
  "routing_reason": "auto: best_value picked ...",
  "billing": {"currency": "USD", "quoted": "0.05", "charged": "0.05", "platform_funded": true},
  "timing": {"duration_ms": 842},
  "attempts": [
    {"provider": "...", "outcome": "provider_timeout", "duration_ms": 8000, "detail": "..."},
    {"provider": "...", "outcome": "succeeded", "duration_ms": 842, "detail": null}
  ],
  "result": {"...": "..."},
  "request_id": "req_...",
  "created_at": "2026-09-02T09:30:00+00:00"
}

hints, when present, provides free guidance about provider pinning, disabled fallback and how to refine a missed people search. It never changes your routing or spending limits. On unbilled no-matches and provider failures, the error also explains budget-excluded alternatives using quotes for the requested input when available.

result is present on success; error ({code, message}) is present on failure. billing.charged is what actually settled: null or lower than quoted when the reservation was released or partly released. A raw endpoint run has kind: "endpoint", capability: null, endpoint set, billing.quoted as the rate-card estimate (or 0), and result.data holding the provider's payload untouched.

A run stopped by a workspace control ends as blocked before any money moves, with controls: [{"control": "daily_spend_guard" | "per_run_limit", "manage": "/v1/spend-guard"}]. It is terminal: tell the user, do not retry until the control changes.

Run lifecycle

created → reserved → running → succeeded | no_match | failed | timed_out

A run still in created, reserved or queued can be cancelled. no_match means at least one provider answered authoritatively that there is no result; whether that bills depends on the capability's stated billing conditions.

A run the router was executing when it restarted stays running for up to about 15 minutes, then ends failed with error interrupted and its hold released: retry it with a new Idempotency-Key.

Repeated misses

On people.search, company.search, company.enrich, company.funding, person.enrich, contact.find and contact.phone, an input that every provider answered with no_match in the last 12 hours is not sent to them again: the run ends no_match at once, charges nothing, and carries repeat_of with the id of the run that missed. A repeated hit always runs, and so does a run with different routing, a provider added since, or routing.fresh: true.

Waterfall failover

With provider: "auto", a provider error, timeout or unbilled no-match falls through to the next-best provider automatically, up to 3 attempts. Only the provider that delivers is charged; every attempt appears in attempts with its outcome. max_cost caps every attempt, and a pinned provider never falls back.

Idempotency

Replaying the same Idempotency-Key returns the original run, marked Idempotent-Replay: true, without executing or billing again. Use it on every retry loop.

Errors you should handle

Code Meaning
insufficient_balance (402) reservation exceeds available balance: top up
cost_limit_exceeded (402) the run's hold exceeds the workspace's per-run ceiling (blocked)
budget_exceeded (402) the daily spend guard would be breached (blocked)
endpoint_not_found (404) unknown raw endpoint for that provider; list them at /v1/catalog/providers/{provider}/endpoints
provider_not_available (409) no live provider for this capability yet (production)
no_match (404) the providers found nothing; billed only if the capability bills no-matches
provider_timeout (504), provider_error (502) all attempts failed; nothing charged

Full list: Errors.

Reading this as an agent? This page as markdown: /docs/api/run.md · every page: /docs/llms.txt