Sandbox
A deterministic, free environment that exposes the full product surface — so your integration works end-to-end before a single BRL is billed.
Every project has a sandbox environment alongside its production environment. The two share the same HTTP API, the same request and response shapes, the same webhook envelope. The only differences are:
- Sandbox keys (
tx_sandbox_...) can only operate on the sandbox environment. - Sandbox sends never reach a real phone — the adapter returns deterministic
sb_<uuid>provider ids. - Sandbox usage is free. Sandbox
UsageRecordrows havecost_brl = 0. - Templates submitted in sandbox auto-approve. No Meta App Review.
- Flows published in sandbox auto-publish. No Meta provider call.
Check sandbox readiness
Use this endpoint before the first send. It reports the current sandbox environment, whether a default sender exists, which scopes are missing, and the exact local listen event type: message.received.
curl https://api.tyxter.com/v1/sandbox/quickstart \
-H "authorization: Bearer $TYXTER_API_KEY"Simulate an inbound message
This is the canonical sandbox inbound endpoint and the sandbox's equivalent of a Meta webhook callback. It returns 202 Accepted, writes an inbound Message row, and fires message.received to your registered webhook endpoints with text normalized at data.content.text.body.
To open the WhatsApp 24-hour service window for a later free-form outbound send, set from to the customer phone you will send to next, and set to to the sandbox sender id used by that outbound request.
curl https://api.tyxter.com/v1/sandbox/inbound-messages \
-H "authorization: Bearer $TYXTER_API_KEY" \
-H "content-type: application/json" \
-H "tyxter-trace-id: diag_open_window" \
-d '{
"channel": "whatsapp",
"from": "+5511999999999",
"to": "sandbox_sender",
"type": "text",
"text": { "body": "Oi, qual o status do meu pedido?" }
}'Opt-out keyword detection
The inbound worker scans the text body against the Brazilian opt-out keyword list (parar, sair, cancelar, stop). When a match fires:
- the contact transitions to
opted_out; - a
contact.opted_outwebhook fires; - subsequent outbound sends to that phone are rejected with
400 contact_opted_out.
You can exercise this path in sandbox by sending an inbound with body: "parar", then attempting an outbound send to the same number.
Simulate a paid payment
Sandbox payment requests can be forced to paid, failed, expired, or cancelled. Use this fixture to test payment completion branches and payment.paid webhooks without relying on a real provider checkout. The connectionless sandbox default is a deterministic hosted-link fixture; Abacate Pay options require a selected Abacate Pay sandbox connection.
curl https://api.tyxter.com/v1/sandbox/payments/pay_123/status \
-H "authorization: Bearer $TYXTER_API_KEY" \
-H "content-type: application/json" \
-H "idempotency-key: fixture-pay-123-paid" \
-d '{ "status": "paid" }'Going to production
- Top up credits from the dashboard.
- Register a Meta connection from the dashboard.
- Submit your templates for approval. Production sends a real template rejection when Meta rejects.
- Swap your key. Same code,
tx_live_...instead oftx_sandbox_....
balance_brl <= -5 receives a 402 credit_balance_exhausted. Sandbox does not enforce this.