Sandbox (test mode)
The sandbox lets you try the API without touching your real data. It is a separate test organization with made-up sample data; test tokens only ever see that data — never your real data, and real tokens never see the sandbox. The database enforces the separation, not only the application.
First response in four steps
| Step | What you do |
|---|---|
| 1 | Administration → API access → Sandbox: create the sandbox (API access must be active, also during the trial). |
| 2 | Generate a test token (prefix m2h_test_, optionally with the scope webhooks:manage) and copy it at once: it is shown only once. |
| 3 | Read with the test token: GET /v1/locations. The response header X-M2H-Sandbox: true confirms the sandbox. |
| 4 | Create a webhook and trigger an event with POST /v1/sandbox/events (see below). |
Getting started
Under Administration → API access → Sandbox create the sandbox (right “API access: full access”, at most one per organization) and generate a test token. It is part of the “API access” add-on and can only be used while API access is active (free trial or booking); use during the trial is not a booking and changes no prices. Without active access no sandbox can be created, and an existing one is blocked: a valid test token then gets `403 sandbox_unavailable` with a pointer to the add-on module (an invalid token still gets `401`); once access is active the same token works again.
curl -H "Authorization: Bearer m2h_test_…" https://api.manage2hero.com/v1/locationsThe address is the same as for real tokens. You recognise the sandbox by the token prefix m2h_test_ and by the response header X-M2H-Sandbox: true, which every response to a test token carries. Test tokens work in every environment (production, staging, development); the sandbox lives in each environment’s own database.
What the sandbox contains
One company, two locations, three garage types, 20 units (twelve rented, two reserved, one blocked, five available), twelve tenants and twelve running contracts. All names and addresses are made up; e-mail addresses end in @sandbox.invalid (never deliverable), and there are no phone numbers (prospects carry a placeholder number).
On top come the data the read endpoints show: three monthly rents per contract (charges, bookings, open items; three tenants are in arrears), 27 incoming payments allocated to the rents, three invoices (open, paid, partially paid), three dunning notices (levels 1 and 2), six electricity meters with three readings each, two damage cases and two tenant reports, two general appointments and two handovers, three prospects, four documents (metadata only, without a file) and six entries in the e-mail history (log rows only, nothing is sent). The bank account is an example IBAN from public documentation, not a connection. “Reset” restores this data; a sandbox that already exists shows it after its next reset.
What the sandbox does not do
No e-mails ever leave the sandbox — not even through the system mailbox. There are no real payments (the incoming payments are made-up examples), no bank connection and no real documents.
Webhooks in the sandbox
A test token may carry the scope webhooks:manage. Sandbox webhooks run through the same delivery service as real ones: signed POST, retries, self-disabling after six failed attempts, replay and pausing, only https on port 443 to public addresses (the same protection against access to internal addresses). Every message carries "livemode": false in the body and the header X-M2H-Livemode: false (real events: true), so test and real events are never mixed up. Delivery only happens with active API access of the organization. As with real webhooks, only production sends; in development and staging the deliveries stay pending.
You trigger an event without any real booking with POST /v1/sandbox/events (body { "type": "payment.received" }, Idempotency-Key required): it is queued for a sample object of the sandbox and sent to your active webhooks subscribing to the type. The history is in GET /v1/webhooks/{id}/deliveries. With a real token the call does not exist (404).
The response names the event id, the chosen sample object, how many webhooks the event went to (recipients, queued) and whether this server sends (delivery: enabled in production, not_in_this_environment in development and staging). Repeat the call with the same Idempotency-Key and you get the same response; nothing is queued twice.
{
"data": {
"event_id": "6f1d3c52-8b0e-4a67-9c21-3d5e7a90b4f8",
"event_type": "payment.received",
"object_id": "b2a4c6e8-1f3d-4b5a-8c7e-9d0f1a2b3c4d",
"recipients": 2,
"queued": 2,
"delivery": "enabled"
}
}curl -X POST https://api.manage2hero.com/v1/sandbox/events \
-H "Authorization: Bearer m2h_test_…" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{"type":"payment.received"}'Differences from real data
| Topic | Sandbox (test token) | Real (live token) |
|---|---|---|
| Data | made-up sample data of a separate test organization | your real data |
| Token prefix | m2h_test_ (valid in every environment) | m2h_live_, m2h_stg_ or m2h_dev_ |
| Response header | X-M2H-Sandbox: true | not present |
| Webhook message | livemode: false, header X-M2H-Livemode: false | livemode: true, header X-M2H-Livemode: true |
| E-mails, payments, bank | never: nothing goes out, there is no connection | depending on configuration |
| Webhook delivery | from production only, only with active API access | from production only |
| Limits | tighter limits and quotas (see below) | standard limits of the API |
| Lifetime | 30 days after the last use, then deleted | unlimited |
Typical error responses
| Response | Meaning |
|---|---|
| 401 | Token invalid, revoked or expired. |
| 403 sandbox_unavailable | The test token is valid, but API access of your organization is not active (trial over, not booked or blocked by the operator). With active access the same token works again. |
| 404 not_found | The call exists only for test tokens (e.g. /v1/sandbox/events), or there is no matching sample object. |
| 409 webhook_limit_reached | There are already 3 webhooks in the sandbox. |
| 409 idempotency_conflict | The same Idempotency-Key was used with different content or a different event type. |
| 422 sandbox_limit_reached | A quota of the sandbox is reached (data volume or simulated events). |
| 429 | Too many requests: wait for the time in the Retry-After header. |
Limits and retention
| Limit | Value |
|---|---|
| Read requests per test token | 60 per minute |
| Write requests per test token | 10 per minute |
| All requests per sandbox | 300 per minute |
| Write requests per sandbox | 500 per day |
| Active test tokens per sandbox | 5 |
| Webhooks per sandbox | at most 3, otherwise 409 webhook_limit_reached |
| Simulated events | at most 100 waiting and 500 per 24 hours per sandbox, otherwise 422 sandbox_limit_reached; deliveries of the sandbox are deleted after 7 days |
| Data volume (tenants, prospects, damages, meter readings, appointments, import records) | at most 3000 combined, otherwise 422 sandbox_limit_reached |
| Imports via API | at most 3 open imports and 5 new ones per hour and token |
| Validity of a test token | 7, 30 or 90 days |
| Lifetime of the sandbox | 30 days after its last use, then deleted |
“Reset” in the management app replaces the sandbox with a fresh one with new sample data (data you created yourself is lost); your active test tokens stay valid and afterwards work in the new sandbox. Once the sandbox has expired, “Create new sandbox” in the management app creates a fresh one; the test tokens of the old one stop working. The operator emergency stop of your organization also blocks the sandbox. Creating and revoking test tokens appear in your organization’s activity log.