# Test mode

> A free test key that returns fixture documents and can force any error code, so your integration and your CI never spend a document.

Section: Get started
Source: https://statementbear.com/docs/test-mode
Product: StatementBear statement parsing API, $0.49 per document, 25 free.

---

A test key returns a fixture document instead of parsing the file you send. It is free, it does not touch your allowance, and it can return any error code on demand.

## Send a request

Take an `sb_test_` key from the [console](https://statementbear.com/dashboard/api). Same endpoint, same host, same headers. Send any PDF: it is checked for size and type and then discarded unread.

cURL:

```bash
# Any PDF will do. It is validated and then discarded.
curl -X POST https://statementbear.com/api/v1/documents \
  -H "Authorization: Bearer $STATEMENTBEAR_TEST_KEY" \
  -H "Content-Type: application/pdf" \
  --data-binary @anything.pdf

# Ask for a specific document, or force an error.
curl -X POST https://statementbear.com/api/v1/documents \
  -H "Authorization: Bearer $STATEMENTBEAR_TEST_KEY" \
  -H "X-Test-Scenario: credit_card" \
  -H "Content-Type: application/pdf" \
  --data-binary @anything.pdf
```

Node.js:

```javascript
// The same client, pointed at a test key. Nothing else changes.
const res = await fetch("https://statementbear.com/api/v1/documents", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.STATEMENTBEAR_TEST_KEY}`,
    "Content-Type": "application/pdf",
    // Omit for the default single-account statement.
    "X-Test-Scenario": "unreconciled",
  },
  body: pdf,
});

const doc = await res.json();
doc.livemode; // false
```

## Scenarios

Send `X-Test-Scenario` to choose what comes back. Omit it for `bank`.

| Scenario | Returns | What it is |
| --- | --- | --- |
| `bank` | `200` | A single current account, reconciled. The default when no header is sent. |
| `credit_card` | `200` | A card statement. Its credits are the repayment and a refund, so it is what to test your income filter against. |
| `multi_account` | `200` | A checking and a savings account on one document, with a transfer printed on both sides. |
| `unreconciled` | `200` | A document whose figures did not reconcile. Exercises your manual review path. |
| `not_a_bank_statement` | `422` | The file was readable and is not a statement. |
| `parse_failed` | `500` | Our error. Retry it. |
| `trial_exhausted` | `402` | The free allowance is spent and no card is on file. |
| `monthly_cap_reached` | `402` | The monthly document ceiling was hit. |
| `canceled` | `402` | Billing for the account was cancelled. |
| `too_many_concurrent_requests` | `429` | Too many documents in flight. Exercises your backoff. |
| `file_too_large` | `413` | Over the size limit, without uploading 20MB to find out. |

An unknown value returns `400 unknown_test_scenario` and the message lists every valid one.

## Telling a fixture apart

Every document carries `livemode`. It is `false` on a fixture and `true` on a real parse. Store it with whatever you parsed for.

200 OK, from a test key:

```json
{
  "id": "doc_test_9c41f0a2b8e37c94",
  "object": "document",
  "livemode": false,
  "currency": "GBP",
  "issuer": { "name": "Monzo", "domain": "monzo.com" },
  "accounts": [ ... ],
  "verification": { "reconciled": true, "issues": [] },
  "usage": { "documents": 1 }
}
```

A fixture's `id` starts `doc_test_`. Everything else about a scenario is the same on every call, so it is safe to assert on in a snapshot test.

## What is the same

- Authentication, including `401` for a missing or revoked key.
- The size, type and content checks on your upload. A mislabelled PNG still returns `415`.
- `Idempotency-Key`, including replays, `Idempotent-Replayed: true` and the `409` after 24 hours.
- The response envelope, the error codes and every header.
- The usage log in your console, so a test call shows up like any other.

## What is different

- The file you send is never read. The response is a fixture.
- Nothing is billed. `X-Billable-Documents` is always `0`.
- Your free allowance and monthly cap are untouched.
- Test keys keep working when billing is cancelled.
- Concurrency is not limited. Up to 120 test requests per minute instead.

> **Test mode does not measure accuracy**
>
> A fixture says nothing about how your own statements come back. Send those to a live key: the first 25 documents are free.

## Idempotency keys

An idempotency key is scoped to the mode that used it. The same string can be used once in test and once for real, and neither replays the other.

## Going live

1. Swap `sb_test_` for `sb_live_` in your secret store.
2. Remove `X-Test-Scenario`. A live key sends it back as `400 test_scenario_on_live_key`, and nothing is parsed or charged.
3. Check that whatever you store records `livemode`.
4. Work through the rest of [Going live](https://statementbear.com/docs/going-live).

---

All documentation: https://statementbear.com/docs/llms.txt
Questions: api@statementbear.com
