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

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

DELETE /v1/sandbox/data parameters
NameInTypeRequiredNotes
Idempotency-KeyheaderstringnoReplay 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

DELETE /v1/sandbox/data responses
StatusMeaning
200Successful Response
401No 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.
403The 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.
409The 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.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

DELETE /v1/sandbox/data response fields
FieldTypeRequiredDescription
deletedobjectyes
resetbooleanyes
wallet_available_microintegeryes

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

POST /v1/sandbox/inbound-faxes parameters
NameInTypeRequiredNotes
Idempotency-KeyheaderstringnoReplay 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

POST /v1/sandbox/inbound-faxes request body
FieldTypeRequiredDescription
fromstring or nullno
pagesintegerno
partial_contentbooleannoRehearse a cut-off transmission.
tostringyes

Responses

POST /v1/sandbox/inbound-faxes responses
StatusMeaning
201Successful Response
401No 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.
403The 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.
409The 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.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

POST /v1/sandbox/inbound-faxes response fields
FieldTypeRequiredDescription
amount_microintegerno
document_idstring or nullyes
fromstring or nullno
idstringyes
notestring or nullno
pagesintegeryes
received_atstringyes
statusstringyes
tostringyes

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

GET /v1/sandbox/scenarios responses
StatusMeaning
200Successful Response
401No 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.
403The 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.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

GET /v1/sandbox/scenarios response fields
FieldTypeRequiredDescription
destinationsarray of objectyes
fixturesarray of object SandboxFixtureyes
scenariosarray of objectyes

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

POST /v1/sandbox/scenarios parameters
NameInTypeRequiredNotes
Idempotency-KeyheaderstringnoReplay 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

POST /v1/sandbox/scenarios request body
FieldTypeRequiredDescription
destinationstring or nullnoApply to one destination; omit for every send.
scenariostringyes

Responses

POST /v1/sandbox/scenarios responses
StatusMeaning
201Successful Response
401No 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.
403The 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.
409The 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.
422A field failed validation: invalid_request, with one entry per field in error.details, or a code that names the rule.
429rate_limited. Retry-After and the RateLimit-* headers say when to try again.

Response fields

POST /v1/sandbox/scenarios response fields
FieldTypeRequiredDescription
created_atstringyes
destinationstring or nullyes
expires_atstringyes
idstringyes
scenariostringyes

Example

curl https://api.faxforagents.com/v1/sandbox/scenarios \
  -X POST \
  -H "Authorization: Bearer ffa_test_..." \
  -H "Content-Type: application/json" \
  -d '{"scenario":"delivered"}'