# Sandbox

> A non-live sandbox organization for testing the AccountFUN API — seeded with demo data, isolated from live books, and resettable to a fresh seed on demand.

Source: https://www.accountfun.com/docs/api/sandbox/

The AccountFUN sandbox is a second, non-live organization reserved for testing the public API. It is seeded with a demo dataset, fully isolated from live books, and can be reset to its fresh seed at any time — so your integration can exercise the whole `/api/v1` surface, including mutations and error paths, without ever touching real financial data.

There is no separate sandbox host and no test-mode API key. The sandbox uses the same base URL, the same API, and the same OAuth flow as a live organization. Each developer gets **one** sandbox.

## What’s in the demo data

* The default chart of accounts.
* Demo contacts at non-deliverable `@example.com` addresses.
* Customer invoices and bills spanning the document lifecycle — draft, approved, and paid.
* Amounts in the organization’s base currency only, with dates relative to the reset day so due-date examples stay meaningful.

## Lockdown guarantees

A sandbox can never reach a real inbox or move real money:

* **No email.** Business sends (documents, statements), invoice reminders, and member invitations are all suppressed or refused.
* **No payments.** Online-payment checkout, billing subscription checkout and portal, and payment-provider (Stripe Connect) onboarding are refused with a sandbox-specific error.
* **Exempt from billing.** Sandbox orgs pass the API’s entitlement gate regardless of subscription state.
* **No export/import.** Data Management export and every import route reject a sandbox organization.
* **Single member.** The sandbox has exactly one member — you, as its admin. Invitations are rejected, and the sandbox is never your default organization.

Sandbox data sits behind the same per-org isolation as live data: a token bound to the sandbox reads and writes only the sandbox, enforced server-side on every query.

## Creating a sandbox

Sign in to AccountFUN and go to **Settings → API & Developers**. The sandbox card sits at the top of the page — click **Create sandbox**.

Any account with a confirmed email that has accepted the terms can create one; the usual organization-creation restrictions apply. You get one sandbox per developer — a second attempt is refused and points at your existing sandbox.

The sandbox appears immediately in your organization switcher, badged **(Sandbox)**.

### Asserting you’re in the sandbox

`GET /api/v1/organisation` reports `"sandbox": true` for a sandbox org:

```bash
curl https://app.accountfun.com/api/v1/organisation \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

```json
{
  "organization": {
    "id": "00000000-0000-0000-0000-000000000000",
    "name": "My Sandbox",
    "baseCurrency": "USD",
    "sandbox": true
  }
}
```

Assert the sandbox marker in test suites

The sandbox shares its base URL with live organizations, so live credentials left in a test config look identical to sandbox ones. Make your mutating test suites fetch the organization on startup, assert `"sandbox": true`, and refuse to run otherwise.

## Authorizing a client into the sandbox

Run the normal OAuth authorize flow — there is nothing sandbox-specific about it. On the consent screen, the organization picker lists your sandbox with a **(Sandbox)** suffix. Choose it deliberately: the token you receive is bound to whichever organization you pick, and works against every `/api/v1` endpoint exactly like a live-org token.

## Resetting the sandbox

| Method | Path                    | Scope           | Description                                   |
| ------ | ----------------------- | --------------- | --------------------------------------------- |
| `POST` | `/api/v1/sandbox/reset` | `sandbox:reset` | Wipe the sandbox and reseed the demo dataset. |

Reset returns the sandbox to its fresh-seed state:

* **All data is wiped** — documents, contacts, journals, and attachments (including files in storage) — and the demo dataset is reseeded. The lock date is cleared too.
* **OAuth grants and tokens survive.** The same access token keeps working against the fresh seed — you do not re-consent after a reset.

### Prerequisites

* Register the `sandbox:reset` scope on your client in the **Developers** area. The consent screen asks for it like any other scope.
* The authorizing user must hold the admin role in the sandbox org — as the creator, that’s you.

### Request

```bash
curl -X POST https://app.accountfun.com/api/v1/sandbox/reset \
  -H "Authorization: Bearer $ACCESS_TOKEN"
```

### Response — `200 OK`

```json
{
  "organizationId": "00000000-0000-0000-0000-000000000000",
  "resetAt": "2026-08-31T12:00:00.000Z",
  "status": "reset-to-fresh-seed"
}
```

The **Reset** button in the Developers area does the same thing — both paths run one shared reset service.

### Reset limits

* **10 resets per hour** per sandbox. Over the cap you get `429` with a `Retry-After` header. The budget is keyed on the sandbox org, not on your token — refreshing tokens does not buy more resets.
* **Concurrent resets conflict.** If a reset is already in flight, a second returns `409`. Retry once the first finishes.

Reset once per test run, not per test

Reset wipes and reseeds the whole organization — it is deliberately heavy and rate-limited. Point each automated suite run at a single reset at the start, and keep per-test isolation in your own fixtures. Also note that refresh tokens still expire on their normal 30-day schedule regardless of resets, so a long-lived automated suite re-consents roughly monthly.

## Limits

The sandbox is for testing, not a free organization:

| Limit                   | Value                                                           |
| ----------------------- | --------------------------------------------------------------- |
| Sandboxes per developer | 1                                                               |
| Demo seed currencies    | Base currency only                                              |
| Transactional documents | 100                                                             |
| Contacts                | 50                                                              |
| Storage                 | Reduced quota                                                   |
| API rate limit          | 60 requests / minute per access token (live organizations: 300) |

Hitting a cap returns a sandbox-specific limit error that names reset as the remedy — reset restores full headroom.

Idle sandboxes are reaped

A sandbox with no activity for 90 consecutive days — no API call on its tokens, no in-app session, no reset — is deleted automatically, and the OAuth grants bound to it are revoked. You can recreate a sandbox at any time.

## Where to go next

* **[API quickstart](https://www.accountfun.com/docs/api/quickstart/)** — the default walkthrough runs against a sandbox.
* **[Authentication](https://www.accountfun.com/docs/api/authentication/)** — the full OAuth flow, token refresh, PKCE, and revocation.
* **[Errors & rate limits](https://www.accountfun.com/docs/api/errors-and-limits/)** — status codes, error shapes, and throttling.
* **[Public API overview](https://www.accountfun.com/docs/api/)** — everything the v1 surface covers today.
