Test mode
An isolated sandbox with familiar API shapes, simulated carriers, and a virtual balance, with no real money.
Every SendIt organization includes an isolated test environment. It mirrors production without touching real money or real carriers. A sk_test_... key activates it, and nothing else needs configuration.
How mode is determined
| Authentication | Mode |
|---|---|
sk_test_... key |
Test |
sk_live_... key |
Live |
| Dashboard session | Live by default; add ?livemode=false to view test data |
Mode is resolved once per request and applies to every read and write. A test request can never read or modify live data, and the reverse is also true, even if it knows the resource’s exact ID.
What test mode simulates
| Feature | Test behavior |
|---|---|
| Carrier calls | Fully simulated — no request ever reaches DHL, FedEx, or Estafeta |
| Tracking numbers | TEST-{CARRIER}-{random} — impossible to confuse with real ones |
| Label URL | https://labels.sendit.mx/test/{shipmentId}/{trackingNumber}.pdf |
| Wallet charges | Debited from the virtual test balance (starts at $10,000 MXN); the real balance is never touched |
| Webhooks | Delivered normally, with "livemode": false in the payload |
| Status progression | Automatic (paced simulation) or manual via the test endpoint |
| Monthly quota | Not counted |
| Rate limits | A separate bucket at 25% of your plan’s limit, with a floor of 10 requests per minute |
Test traffic never consumes the live bucket. On the FREE plan, for example, a quote limit of 20/min becomes 10/min for sk_test_.... The read, write, and quote buckets stay independent. See rate limits.
Watch a full journey in minutes
Every tracked parcel advances on its own, simulating a real journey anchored to when tracking started:
LABEL_CREATED (+0 min) → PICKED_UP (~+4) → IN_TRANSIT (~+9) → OUT_FOR_DELIVERY (~+14) → DELIVERED (~+27)
Some tracking numbers deliver straight from IN_TRANSIT, as they do in the real world. You can watch the whole journey, with the webhooks for each transition, complete in under half an hour and without calling anything.
Advance status manually
Don’t want to wait? Advance one step per call:
curl -X POST https://api.sendit.mx/v1/shipments/{id}/test/advance-status \
-H "X-API-Key: sk_test_..."
LABEL_PURCHASED → READY_FOR_PICKUP → PICKED_UP → IN_TRANSIT → OUT_FOR_DELIVERY → DELIVERED
Each call returns the new status and records the event in the shipment’s history.
Simulate failures and returns
Include a keyword in the recipient’s contactName to trigger alternate progressions:
| Keyword in contactName | Progression |
|---|---|
SENDIT_FAIL |
... IN_TRANSIT → FAILED |
SENDIT_RETURN |
... IN_TRANSIT → RETURNED |
| (none) | ... → DELIVERED (default) |
{ "contactName": "Test Customer SENDIT_FAIL", "...": "..." }
Use it to exercise your failed-delivery and return flows before they happen with real customers.
Tell test events apart in your webhooks
Every webhook payload originating in test mode carries livemode: false:
{
"id": "evt_...",
"type": "shipment.tracking.updated",
"livemode": false,
"data": { "...": "..." }
}
Your handler should check livemode to route events correctly between your staging and production environments.
Reset the test balance
When the virtual balance runs low, restore it. This works on any organization and has no limit:
curl -X POST https://api.sendit.mx/v1/wallet/test/reset \
-H "Authorization: Bearer <jwt>"
{
"data": { "balance": 10000, "currency": "MXN" },
"message": "Test wallet reset to $10,000 MXN"
}
The real balance is never affected by a reset.
Read test data from a dashboard session
Add ?livemode=false to list endpoints to read test data with a JWT:
GET /v1/shipments?livemode=false
Authorization: Bearer <jwt>
It accepts only the literal strings true and false. Any other value, including 0, 1, yes, or empty, returns 400 VALIDATION_ERROR. Omit it and you read live data.
The parameter does not apply to API keys: the key’s own environment always wins. It is accepted and ignored.
| Endpoint | Effect of ?livemode=false |
|---|---|
GET /v1/shipments |
Test shipments |
GET /v1/trackers |
Test trackers |
GET /v1/wallet/transactions, GET /v1/wallet/summary, GET /v1/wallet/balance |
Test-balance movements |
GET /v1/orders |
Test orders |
GET /v1/api-requests |
Test-mode request logs |
GET /v1/webhook-endpoints/:id/events |
Test deliveries |
GET /v1/billing/invoices, /v1/products, /v1/carrier-services |
Accepted, but these resources carry no mode: the result is the same either way |
GET /v1/pickups |
Returns 400 LIVE_MODE_REQUIRED |
What is live-only
Some operations are rejected in test mode before they touch any resource:
- Wallet funding. The Stripe, CLABE, card, PayPal, and OXXO routes return
400 LIVE_MODE_REQUIREDbecause they create or reveal real payment resources. For sandbox funds, use the reset above. - Pickups (recolecciones). Schedule, list, get, cancel, and refresh all return
400 LIVE_MODE_REQUIRED. The one exception isGET /v1/pickups/carriers, which returns capability data only and carries no mode. - Public tracking. Test tokens never resolve on the public page.
What is shared across modes
Addresses, saved packages, and postal codes are shared resources: when creating a test shipment you can reference any saved address, regardless of the mode it was created in. Organization settings, members, and invitations are shared too.