Guides
Sandbox and simulator
How the sandbox works, the simulator that moves visits forward, manual mode with X-Sandbox-Simulation, the /sandbox/visits/{id}/advance and /sandbox/reset endpoints, and the recommended test scenarios.
Last reviewed: October 1, 2026
What it is
The sandbox is an isolated test environment for building and validating your integration without touching anything real: a separate deployment of the same API, with its own database, at https://sandbox-api.saludenruta.es/api/integrations/v1. There:
- Paths, fields, errors, signatures and webhook retries are the same as in production.
- Your provider is configured as in production: same type, same service catalogue (with different
idvalues; use the codes, see environments), same end insurers and the same case number requirement. - There's no dispatch centre and no real units: visits are moved forward by a simulator.
- No emails, notifications, prescriptions or staff alerts are sent, and nothing is invoiced.
- Keys are
srd_test_…. A production key here, or a test key in production, gets a401/errors/provider-api/wrong-environment. - Every response carries
X-Serad-Environment: sandbox.
The simulator
When you create a visit in the sandbox, the simulator moves it forward on its own, one step every 20 seconds:
PENDING ──20 s──▶ ASSIGNED ──20 s──▶ EN_ROUTE ──20 s──▶ ON_SITE ──20 s──▶ FINISHED (outcome: ATTENDED)- Each step sends the same webhooks as production (
visit.assigned,visit.en_route…), with the same shape, signature and retries. - On assignment, the simulator sets a fictitious unit for the day, medical or nursing depending on the service type, so
unitTypeandunitcome out just as in production. - While the visit is en route,
GET /visits/{id}/etareturns a simulated estimated time (arrival is the next step). - The final outcome is always
ATTENDED. For other outcomes, or to cancel, use manual simulation. - Visits end in
FINISHED: there's no validation or invoicing in the sandbox, so they never reachINVOICEDandvisit.invoicedis never sent.
Manual simulation
To test a specific scenario (cancelling with the unit assigned, a visit that stays en route for a good while), create the visit with the X-Sandbox-Simulation: manual header. That visit doesn't move forward on its own: you move it.
curl -X POST "$SERAD_API_URL/visits" \
-H "X-Api-Key: $SERAD_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: prueba-manual-0001" \
-H "X-Sandbox-Simulation: manual" \
-d @aviso.jsonAdvance a visit
POST /sandbox/visits/{id}/advance (scope visits:write) moves it to the next status, or to the one you specify in to:
# To the next status (PENDING → ASSIGNED)
curl -X POST "$SERAD_API_URL/sandbox/visits/c6a3c348-40b6-4158-a44a-7d85f01a0201/advance" \
-H "X-Api-Key: $SERAD_API_KEY"
# Straight to on site (goes through the intermediate statuses)
curl -X POST "$SERAD_API_URL/sandbox/visits/c6a3c348-40b6-4158-a44a-7d85f01a0201/advance" \
-H "X-Api-Key: $SERAD_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "to": "ON_SITE" }'to | Effect |
|---|---|
| (no body) | To the next status in the flow. |
ASSIGNED | Assigns a fictitious unit. |
EN_ROUTE | The unit sets off. |
ON_SITE | The unit arrives. |
FINISHED | Completes the care with outcome: ATTENDED. |
CANCELLED | Cancels the visit (from PENDING, ASSIGNED or EN_ROUTE), as if the dispatch centre had done it, free of charge. |
- If you skip statuses (from
PENDINGtoON_SITE), the visit goes through the intermediate ones and you receive the webhook for each, in order. - The response is the visit with its timeline, like
GET /visits/{id}. - Asking for an earlier or disallowed status (from
EN_ROUTEtoASSIGNED, or moving a visit that has already finished) returns400/errors/sandbox/visit-not-advanceable, with a message such as "El aviso AV-000004 está en camino y no puede pasar a «asignado».". - You can also use
advanceon automatic visits so you don't have to wait the 20 seconds.
Reset the environment
POST /sandbox/reset (scope visits:write) deletes all your test visits and the patients that only appeared in them, so you can start a test run from scratch:
curl -X POST "$SERAD_API_URL/sandbox/reset" \
-H "X-Api-Key: $SERAD_API_KEY"{ "status": 200, "data": { "deletedVisits": 12 }, "message": "Operación realizada correctamente" }It only affects your organisation and only the sandbox. Your catalogue, keys and webhooks are left untouched.
Recommended test scenarios
Before asking for your production key, run through these scenarios in the sandbox. They're the ones we'll go through with you during validation (see the go-live checklist).
| # | Scenario | How | What to check |
|---|---|---|---|
| 1 | Complete urgent visit | Create with priority: "URGENT", automatic mode. | You receive, in this logical order, visit.created, visit.assigned, visit.en_route, visit.on_site and visit.finished, all with a valid signature; your system reflects each status and the outcome. |
| 2 | Scheduled appointment | Create with priority: "SCHEDULED", scheduledFrom tomorrow at 9:00 (+02:00/+01:00) and estimatedMinutes: 40. | The response includes scheduledFrom and scheduledTo (= start + 40 min). Also try an appointment in the past and one more than 90 days ahead: 400. |
| 3 | Allowed cancellation | Create in manual mode, advance to ASSIGNED, then POST /visits/{id}/cancel. | 200, status: "CANCELLED", cancelledWithCharge: false and the visit.cancelled webhook. |
| 4 | Disallowed cancellation | Create in manual mode, advance to EN_ROUTE, then cancel. | 409 /errors/provider/visit-not-cancellable, and your system tells the operator to call the dispatch centre. |
| 5 | Repeat patient | Two visits with the same document and date of birth. | Both are created without errors. In each one, patientName is the name you sent for that visit. |
| 6 | Idempotency | Repeat the same request with the same Idempotency-Key; then the same key with a different reason. | First 201; the repeat, 200 with the same id and Idempotent-Replayed: true; with a different body, 409 idempotency-key-reused. No duplicate visits. |
| 7 | Webhook down and retries | Make your receiver respond 500 (or shut it down), create a visit and bring it back up a minute later. | You receive the retries with the same X-Serad-Delivery and new signatures; your system processes each event only once. |
| 8 | Bad signature | Send your receiver a request with a modified body or a timestamp from 10 minutes ago. | Your receiver responds 401 and processes nothing. |
| 9 | Validation errors | Create without reason, with a DNI with the wrong letter, with estimatedMinutes: 45 and with a made-up field. | 400 with errors[] pointing at the field; your system shows the problem without retrying. |
| 10 | Rate limit | Send more requests per minute than your X-RateLimit-Limit. | 429 with Retry-After; your client waits and retries. |
| 11 | Key from the other environment | Call the sandbox with an srd_live_… key (or the other way round). | 401 wrong-environment; your configuration prevents this from happening by mistake. |
For each scenario, start with POST /sandbox/reset if you want a clean environment.
Test data
Use made-up data but in a valid format, because it's validated just as in production:
| Item | Example value |
|---|---|
| Valid DNI | 12345678Z, 00000000T, 99999999R |
| Valid NIE | X1234567L, Y0000000Z |
| Invalid DNI (wrong letter) | 12345678A |
| Phone | +34600000001 … +34600000099 |
| Address | Any real street in Madrid, for example Calle de Alcalá 120, 28009, Madrid. |
| Date of birth | 1948-05-17, 2019-03-02 (paediatrics, with documentType: "NONE") |
| Case number | TEST-0001, TEST-0002… |
Something missing or not matching what you see? Email our technical team: info@saludenruta.es.
