Sandbox API reference
Updated
4 operations of the Fax for Agents API, from the OpenAPI document version 1.20.0, snapshot of 2026-11-13. The base URL is https://api.faxforagents.com/v1. This is one group of the full reference.
- A synthetic inbound fax goes through the same pipeline as a real one.
- A scenario selects the outcome of the next sends, for one destination or for every send.
- The sandbox routes are for test keys, and a live key is refused with sandbox_only.
Operations
DELETE /v1/sandbox/dataReset this organization's test dataPOST /v1/sandbox/inbound-faxesCreate a synthetic inbound fax (the same pipeline as a real one)GET /v1/sandbox/scenariosThe scenario catalog and active fixturesPOST /v1/sandbox/scenariosSelect the outcome of the next sends
What does the sandbox group add to a test key?
Control. Test keys already send to the sandbox: no carrier, no cost, and the outcome follows from the destination number. The four operations here let a test choose the outcome, receive a fax it did not have to send, and start again from clean data.
The rest of the API is the same in the sandbox. Documents, quotes, faxes, webhooks and the wallet behave as they do live, with test credit in place of money.
How do I choose the outcome of a send?
POST /v1/sandbox/scenarios takes a scenario and, when you want one, a destination. With a destination the fixture applies to sends to that number. Without one it applies to every send of the organization that has no more specific fixture. The answer has the fixture's id and expires_at.
GET /v1/sandbox/scenarios returns the catalog: the scenario names, the scripted destinations and the fixtures that are active now. The names cover every way a fax can end, including the two a test rarely meets by chance, partial and unknown. The sandbox guide has the table of numbers and what each one does.
How do I rehearse receiving?
POST /v1/sandbox/inbound-faxes creates a synthetic inbound fax. The body takes to, and optionally from, pages and partial_content, which rehearses a cut-off transmission. The summary says the fax goes through the same pipeline as a real one. A private document is stored, an inbox item appears, and a fax.received event fires with its download reference.
A receiver built against this call is finished before the organization owns a number. The item is read through the inbox group like any other.
What does a reset remove?
DELETE /v1/sandbox/data resets the organization's test data. The answer has deleted, which counts what was removed, and wallet_available_micro, the test wallet after the reset. Live data is not touched: the call works on the test environment only.
Run it between test suites, so that one run's faxes and fixtures do not leak into the next.
Which credential do sandbox calls take?
A test key with sandbox:write, which the default API key carries. The four operations all name that scope, the catalog read included. A live key is refused with 403 sandbox_only. A signed-in session reaches the sandbox when it sends X-FFA-Environment: test, which is also the default for a session.
What does the sandbox not simulate?
A real fax line. A scripted outcome arrives at once or on the worker's schedule, not after a real call. Timing measured against the sandbox says nothing about a carrier. What the sandbox proves is your handling of each state, and of the events and holds that go with it.
How do sandbox events reach a webhook?
The same way live ones do. A test send fires fax.queued, fax.started and its ending event, with the same signature scheme as a live delivery. Register the receiver in the webhooks group with the test key, and it gets the events of test faxes only.
Each operation in detail
DELETE /v1/sandbox/data
Reset this organization's test data
Credential: an API key or a signed-in session. Scope: sandbox:write.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
Idempotency-Key | header | string | no | Replay the first successful response for the same request within 24 hours. Reusing the idempotency header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: true on replay. |
Responses
| Status | Meaning |
|---|---|
200 | Successful Response |
401 | No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused. |
403 | The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required. |
409 | The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state. |
422 | A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule. |
429 | rate_limited. Retry-After and the RateLimit-* headers say when to try again. |
Response fields
| Field | Type | Required | Description |
|---|---|---|---|
deleted | object | yes | |
reset | boolean | yes | |
wallet_available_micro | integer | yes |
Example
curl https://api.faxforagents.com/v1/sandbox/data \
-X DELETE \
-H "Authorization: Bearer ffa_test_..."
POST /v1/sandbox/inbound-faxes
Create a synthetic inbound fax (the same pipeline as a real one)
A synthetic PDF goes through ``inbox.service.receive_now``: the private document, the inbox item, the ``fax.received`` event with its download reference, exactly as a carrier delivery (M8).
Credential: an API key or a signed-in session. Scope: sandbox:write.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
Idempotency-Key | header | string | no | Replay the first successful response for the same request within 24 hours. Reusing the idempotency header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: true on replay. |
Request body application/json, required
| Field | Type | Required | Description |
|---|---|---|---|
from | string or null | no | |
pages | integer | no | |
partial_content | boolean | no | Rehearse a cut-off transmission. |
to | string | yes |
Responses
| Status | Meaning |
|---|---|
201 | Successful Response |
401 | No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused. |
403 | The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required. |
409 | The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state. |
422 | A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule. |
429 | rate_limited. Retry-After and the RateLimit-* headers say when to try again. |
Response fields
| Field | Type | Required | Description |
|---|---|---|---|
amount_micro | integer | no | |
document_id | string or null | yes | |
from | string or null | no | |
id | string | yes | |
note | string or null | no | |
pages | integer | yes | |
received_at | string | yes | |
status | string | yes | |
to | string | yes |
Example
curl https://api.faxforagents.com/v1/sandbox/inbound-faxes \
-X POST \
-H "Authorization: Bearer ffa_test_..." \
-H "Content-Type: application/json" \
-d '{"to":"+15550000001"}'
GET /v1/sandbox/scenarios
The scenario catalog and active fixtures
Credential: an API key or a signed-in session. Scope: sandbox:write.
Responses
| Status | Meaning |
|---|---|
200 | Successful Response |
401 | No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused. |
403 | The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required. |
429 | rate_limited. Retry-After and the RateLimit-* headers say when to try again. |
Response fields
| Field | Type | Required | Description |
|---|---|---|---|
destinations | array of object | yes | |
fixtures | array of object SandboxFixture | yes | |
scenarios | array of object | yes |
Example
curl https://api.faxforagents.com/v1/sandbox/scenarios \
-H "Authorization: Bearer ffa_test_..."
POST /v1/sandbox/scenarios
Select the outcome of the next sends
Credential: an API key or a signed-in session. Scope: sandbox:write.
Parameters
| Name | In | Type | Required | Notes |
|---|---|---|---|---|
Idempotency-Key | header | string | no | Replay the first successful response for the same request within 24 hours. Reusing the idempotency header with a different request returns 409 idempotency_key_conflict. Responses include Idempotent-Replayed: true on replay. |
Request body application/json, required
| Field | Type | Required | Description |
|---|---|---|---|
destination | string or null | no | Apply to one destination; omit for every send. |
scenario | string | yes |
Responses
| Status | Meaning |
|---|---|
201 | Successful Response |
401 | No credential, or one that was not accepted: invalid_token for an API key, unauthenticated where a signed-in session is needed. error.details.reason says why a key was refused. |
403 | The credential is valid and is not allowed to do this. scope_missing puts the scope in error.details.scope. Other codes are agent_forbidden and approval_required. A session can also meet csrf_invalid and reauth_required. |
409 | The resource is in a state that refuses the call, or the Idempotency-Key was used with a different request (idempotency_key_conflict). error.code names the state. |
422 | A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule. |
429 | rate_limited. Retry-After and the RateLimit-* headers say when to try again. |
Response fields
| Field | Type | Required | Description |
|---|---|---|---|
created_at | string | yes | |
destination | string or null | yes | |
expires_at | string | yes | |
id | string | yes | |
scenario | string | yes |
Example
curl https://api.faxforagents.com/v1/sandbox/scenarios \
-X POST \
-H "Authorization: Bearer ffa_test_..." \
-H "Content-Type: application/json" \
-d '{"scenario":"delivered"}'