Skip to main content
Self-serve signup is open. Anyone can POST /v1/signup and get a working API key in one call, with no review and no card on file. That is only safe because of one gate: sandbox confinement. A confined request can only check out against merchants we control.

Two independent triggers

A request to POST /v1/checkouts is confined if either is true:

The account is sandbox-only

Every self-serve signup starts here. Confinement applies to every key the account holds, including ck_live_ ones it cannot create yet.

The request used a test key

A ck_test_ key is confined even on a fully activated account. Key mode is a property of the credential, not the account.
Either one on its own stops a real purchase. That is the point of having two: account status answers “is this customer allowed to spend real money”, key mode answers “is this specific credential allowed to”, and a mistake in one should not be enough.

Why the gate is where it is

Every other guardrail on this platform limits how much money moves or how fast. maxCost caps a purchase. Quotas cap concurrency and daily volume. Cards are single-use by policy. None of those decide whether real money is reachable at all — and an agentic checkout platform that hands a stranger unrestricted access to real merchants is, functionally, carding infrastructure. Sandbox confinement is the one control that answers that question, so it is on by default and cannot be turned off from inside the API.

What you can do in the sandbox

Everything. That is the point — it is not a mock.
  • Real browser, real merchant storefront, real checkout pages.
  • The real engine, chosen the same way, driving the same code paths.
  • A real variant prompt with the store’s real options.
  • Real card entry into real payment iframes.
  • A genuinely placed order on the sandbox store, with a real confirmation page and a real merchantOrderId.
  • Webhooks, live view, buyer profiles, buyer sessions, usage — all identical.
Your integration in the sandbox is the integration you ship. No code changes when you go live; only the target URLs and the key change.

What you cannot do

POST /v1/checkouts against any other host returns 403, and the body tells you the offending host, which of the two triggers applied, and where you can check out:
On an activated account using a test key, the same 403 carries the other reason:
Read sandboxMerchants from the response rather than parsing the message. Host matching covers subdomains and ignores a leading www..
target.allowedDomains is checked by the same gate, not just target.url. Naming a real merchant there produces the same 403, with that host named as the offender.This matters because allowedDomains is what widens the engine’s navigation guard. Checking only the target URL would leave a route straight off the sandbox store — start at the sandbox host, declare a real merchant as an allowed domain, and walk over.
POST /v1/api-keys with {"mode":"live"} returns 403:
{"mode":"test"} works, and you can create as many test keys as you like.Two separate refusals live here, and both are 403: this one, about the account, and a second about the credential — a ck_test_ key cannot issue a ck_live_ key even on an activated account. See minting a live key.

Signing up

Response (201)
Both body fields are optional; email is only ever used to reach you if your agents start doing something alarming. It is not a login — there is no password and no session. sandboxMerchants is the authoritative list of hosts you may target. Read it from the response rather than hard-coding it.

Signup limits

This is the one unauthenticated write on the API, so it is rate-limited on two axes: Signup can also be closed entirely, in which case every request gets 403 Self-serve signup is closed. The per-IP window is the cheap common case; the daily cap is the backstop that still holds against a rotating-IP flood.

Testing failure paths

The sandbox is the right place to prove your error handling, and every path is reachable:
Exercise decline and user_action_expired before you ship. They are the two states most integrations forget, and both are guaranteed to happen in production — an operator will walk away from a card prompt at some point.

Reaching real merchants

Two things have to be true: the account must be activated, and the request must use a ck_live_ key.

Activating the account

Activation is self-serve, from the dashboard at app.farthing.ai, and fully automated: hold a paid plan, verify your identity through Stripe, accept the AUP, and the workspace flips to live the moment all three are true. No approval queue. The whole journey is on Going live. One thing to know before you start: activation is driven from a dashboard workspace. A tenant created with the curl-only POST /v1/signup below has no dashboard account attached and cannot be activated — sign up at the dashboard when you intend to go live.

After activation

  • sandboxOnly is false, so the account-level refusal on POST /v1/api-keys with {"mode":"live"} is gone.
  • Mint your first ck_live_ key from the dashboard. A ck_test_ key still cannot issue a live one — authority passes down, never sideways — but the dashboard session of an activated workspace counts as a live credential, and any live key can mint more. See minting a live key.
  • Your existing ck_test_ keys are still confined to sandbox merchants. That is not a leftover restriction to work around — it is the point. Keep them for CI and staging.
  • Point production at a new ck_live_ key. Nothing else in your integration changes.
  • Quotas still apply: concurrency comes from your plan (Personal 1, Business 5, Scale 15), daily volume defaults to 200 per 24 hours on every plan, and Personal caps a single checkout at $200. See Pricing — the daily cap is raisable per-account.
Because a test key stays confined forever, the safest production layout is: ck_live_ in one place only — the deployed service that is supposed to buy things — and ck_test_ everywhere else. A leaked or misconfigured test key cannot cost anyone money.