# Yappy API sandbox

Everything under `/api/v1/sandbox` behaves exactly like the live API and touches
nothing real. Use it to build and test an integration, and to prove your error
handling works before you find out in production.

## Base URL

```
https://yappy.biz/api/v1/sandbox
```

Every path under `/api/v1` exists under it. `GET /api/v1/sandbox/releases/latest`
mirrors `GET /api/v1/releases/latest`, with fixture data instead of live data:

```bash
curl -s https://yappy.biz/api/v1/sandbox/releases/latest
```

```json
{
  "object": "release",
  "version": "0.0.0-sandbox",
  "notes": "Fixture release. Deterministic, never changes, safe to assert against.",
  "published_at": "2026-01-01",
  "download_url": "https://yappy.biz/api/v1/sandbox/download",
  "sandbox": true
}
```

Every sandbox response carries `"sandbox": true` in the body and an
`X-Yappy-Environment: sandbox` header, so a misconfigured client that is pointing
at the wrong base URL is obvious in one glance at the response rather than three
hours into an incident.

## Deterministic fixtures

Sandbox data never changes. That is the point: you can assert on exact values in
a test suite without it breaking the day a real release ships. Version is always
`0.0.0-sandbox`, the docs corpus is a fixed three pages, and the FAQ is a fixed
two entries.

## Forcing errors

Every error path is reachable on demand with the `?simulate=` parameter, so you
can test your retry and backoff logic without waiting for a real failure:

| `simulate` | Response |
|---|---|
| `rate_limited` | `429` with `Retry-After: 3` and the rate-limit headers |
| `invalid_request` | `400` with a populated `errors[]` array |
| `not_found` | `404` |
| `invalid_token` | `401` with a `WWW-Authenticate` challenge |
| `insufficient_scope` | `403` naming the missing scope |
| `internal_error` | `500` |
| `upstream_unavailable` | `503` with `Retry-After` |
| `slow` | A successful response after a two-second delay |

```bash
curl -si "https://yappy.biz/api/v1/sandbox/releases?simulate=rate_limited"
```

## Async jobs

`POST /api/v1/sandbox/exports` returns a `202` and a job that reports `queued`,
then `running`, then `completed` across successive polls — so you can exercise a
polling loop that actually loops, instead of one that gets `completed` on the
first try and never tests the interesting path.

## Rate limits

The sandbox has its own bucket and does not consume your production allowance.
It is limited more tightly on purpose — 60 requests per minute — because a test
suite that needs more than that is testing our infrastructure rather than your
integration.

## What is not simulated

There is no sandbox for dictation, because there is no production endpoint for
dictation either — speech recognition runs on the user's Mac. The sandbox mirrors
the metadata API and nothing else.

---

Canonical: https://yappy.biz/developers/sandbox/ · Last updated: 2026-08-25
