# Evaluate BillerAPI

One page, three separate ways to evaluate. Each branch below is a different availability state, so pick exactly one and follow it end to end. Mixing steps from two branches is the most common way an evaluation stalls.

Every branch ends at the same proof: an authoritative normalized bill you did not hand-write, and a signature-verified webhook that announced it.

> **Note — This page makes no timing promise**
> No step here claims an elapsed time. Journey timing is measured server-side, and no measured figure has been published, so an unmeasured duration would be a guess presented as a fact. Timing returns to this page when it is measured, not before.

## Pick one branch

| Branch | What you need | Primary action | Fallback |
| --- | --- | --- | --- |
| Account-free | Nothing | Create a claimable sandbox with one unauthenticated request | Read the machine-readable contract instead of running it |
| Owned account | An email address | Sign up in the Client Portal and use the sandbox key minted with the account | Claim a sandbox you already created account-free |
| Consented agent | A human who keeps identity, consent, and production access | Give the agent the read-only public contract | The human runs a branch above and hands over a sandbox key only |

Each branch has exactly one primary action and exactly one fallback. If the primary action is unavailable to you, take that branch's fallback rather than switching branches mid-flight.

## Branch 1 — Account-free

No signup, no email, no key of your own. The claimable-sandbox route is public, and production refuses it today: account-free creation ships fail-closed until per-sandbox quotas land, so the request below answers `501 NOT_IMPLEMENTED` on both hosts. Where the switch is on, the route mints a sandbox client with its own `bak_test_` key plus a one-shot claim URL you can redeem later. For a key you can use right now, take Branch 2.

### Primary action

**cURL**

```curl
curl -X POST https://sandbox.api.billerapi.com/v1/sandboxes \
  -H "Content-Type: application/json" \
  -d '{}'
```

Where creation is enabled, the response carries `sandbox_api_key` and `claim_url` exactly once and neither value can be recovered afterwards, so store both before you continue. Use the key as a Bearer token against the sandbox host, then continue from the SDK install step of [Getting Started](/docs/guides/getting-started).

Creation is served on the sandbox host and on the production host alike, so the whole sequence on this page — create, link, bill, proof — runs against one host. Both answer `501 NOT_IMPLEMENTED` while the switch is off, as they do today, and the answer comes from the same switch rather than from each host's own opinion.

### Fallback

If you cannot make an outbound request yet, read the contract instead of running it. Every docs page serves its raw Markdown when you append `.md` to the URL, and [llms.txt](/llms.txt) indexes the whole corpus. That fallback needs no key, no account, and no write of any kind.

> **Warning — The claim URL is a capability, not a link to share**
> Anyone holding the claim URL can attach that sandbox and its data to their own account, so treat it exactly like a credential. An unclaimed sandbox is deleted at expiry and its remaining data becomes permanently inaccessible.

> **Note — The command-line entry points are published**
> `billerapi-cli` 1.1.0 is on npm and calls the same public route. `npx -y billerapi-cli@1.1.0 billers list` with your `bak_test_` key lists the sandbox billers; the [CLI guide](/docs/guides/cli) carries the release state. The CLI calls that same creation route, so it meets the same refusal while the switch is off.

## Branch 2 — Owned account

Take this branch when you want a durable account, a key you can rotate, and a path to production later.

### Primary action

Sign up in the Client Portal. Signup signs you in and mints a sandbox `bak_test_` key immediately, and [Getting Started](/docs/guides/getting-started) walks the rest of the path from that key.

### Fallback

If you already ran Branch 1, open the one-shot claim URL from that response instead of signing up cold. Claiming attaches the existing sandbox and everything already in it to your new account, and the sandbox keeps its client id while its key is rotated.

> **Note — Email verification gates production, not sandbox**
> Verifying your email and finishing the [go-live checklist](/docs/guides/go-live) is required before you send live traffic. Neither one blocks signing in or using the sandbox.

## Branch 3 — Consented agent

An agent may read the public contract on its own. It may not become you. The human keeps identity, legal acceptance, key custody, and production access, and grants the agent one narrow scope at a time.

### Primary action

Point the agent at the machine-readable surface: [llms.txt](/llms.txt) for the index, any page's `.md` variant for that page's full text, and [openapi.json](/docs/openapi.json) for the wire contract. That surface is read-only and needs no credential, so an agent can summarize the public biller catalog and the error taxonomy without ever acting as you.

### Fallback

When the agent has to execute something, narrow the work rather than widening its authority. Run Branch 1 or Branch 2 yourself, then hand the agent only the `bak_test_` sandbox key. Keep the claim URL, your password, and every production key out of the agent's context.

> **Warning — Consent is never delegated**
> An agent must not create an account, handle a password, accept legal documents, choose a consent scope, or use a production key on your behalf. Those acts bind a human, and a machine cannot consent for one.

> **Note — The hosted agent plane is live**
> The hosted MCP endpoint at https://mcp.billerapi.com/mcp serves the documentation and sandbox-catalog tools (`search_docs`, `get_doc`, `list_billers`, `get_biller`), and the CLI agent-setup command is published. Account-free sandbox creation through the MCP stays fail-closed until per-sandbox quotas land (#7388); use the Branch 1 request for that. The [agents overview](/docs/agents) carries the current release state.

## Where an evaluation ends

The proof is server-side rather than a screenshot, and it is the same three stages on every branch:

1. **Sandbox proof** — an authenticated call returns a resource your own key owns.
2. **First normalized bill** — a bill lands on a linked sandbox account in the normalized shape, and you did not write it.
3. **Signed webhook** — your endpoint receives that bill's event and signature verification accepts it.

The [Webhooks](/docs/guides/webhooks) guide covers the signature scheme and the [webhook delivery](/docs/concepts/webhook-delivery) concept page covers the retry schedule.

### Ask the server whether it happened

You do not have to take your own word for it. Ask the server:

```curl
curl https://sandbox.api.billerapi.com/v1/sandboxes/{sandbox_id}/evaluation-proof \
  -H "Authorization: Bearer $BILLERAPI_SANDBOX_KEY"
```

`sandbox_id` is the `client_id` your sandbox was created with, and the key must be that sandbox's own key: a credential can only ask about the sandbox it authenticates as. Send it to the sandbox host with your `bak_test_` key; the production host answers the same path for a production key and refuses a sandbox one, because the two hosts hold different evidence and neither borrows the other's authority.

The response reports `proof_state`:

| `proof_state` | What the server established |
| --- | --- |
| `PROVEN` | A bill whose normalization the extraction pipeline produced exists, and a signature-verified webhook delivery for it was recorded. Both halves, from server-side evidence. |
| `PENDING` | One half is established and the other is not. `stages_reached` names the ones that are. |
| `NOT_STARTED` | The reads succeeded and found nothing yet. |

Three things this endpoint deliberately will not do:

- A bill you created yourself with `POST /v1/bills` never counts. That route records the bill as client-supplied, and a proof that accepted it would only be certifying your own request back to you.
- A webhook you fired at yourself with the synthetic test-fire endpoint never completes the proof. It reports as `source_kind: "synthetic_fire"` and proves your signature verification works, not that the pipeline announced anything.
- A downstream read that fails answers `503`, never `404` and never an empty proof. "There is nothing here" and "I could not tell" are different answers and stay different.

Two host-specific details, stated rather than left to be discovered:

- On the sandbox host the delivery instant is reported as `signed_delivery_accepted_at`, not `signature_verified_at`. The sandbox signs the body and your own endpoint accepts it; it does not watch you verify the signature, and it will not name a fact it cannot observe. The production host, whose capture path verifies the signature itself, reports `signature_verified_at`.
- Sandbox evaluation evidence is in-memory, like the rest of the sandbox. If the sandbox service restarts, a sandbox that reported `PROVEN` reports `NOT_STARTED` again. Re-run the link to re-establish it.

## Not part of an evaluation today

- **Payment execution.** Pay endpoints answer `501 PAYMENT_EXECUTION_NOT_AVAILABLE` and create no payment attempt, so model the flow but build no fulfillment against it.
- **Operator-driven webhook replay.** No console control re-sends an already-delivered event. Reconcile with [bill sync](/docs/api/bill-sync) rather than waiting for a redelivery you cannot trigger.
- **Real-time support during evaluation.** Report anything wrong through [docs feedback](/docs/guides/feedback); no staffed interactive channel is offered with evaluation.

## Next Steps

- [Getting Started](/docs/guides/getting-started) — The full walkthrough every branch converges on
- [Choose an Integration](/docs/guides/integration-paths) — Map an outcome to the right API surface once you have a key
- [Environments](/docs/concepts/environments) — Sandbox and production hosts, keys, and prefixes
- [Go Live](/docs/guides/go-live) — What production access requires beyond a working sandbox
