Get started
Production and sandbox environments
Differences between production and the sandbox, key prefixes, the X-Serad-Environment header, why identifiers differ between environments and the go-live checklist.
Last reviewed: October 1, 2026
Two separate environments
The API exists in two completely independent environments: two separate deployments of the same API, each with its own database. Nothing you do in one ever shows up in the other.
| Production | Sandbox | |
|---|---|---|
| Base URL | https://api.saludenruta.es/api/integrations/v1 | https://sandbox-api.saludenruta.es/api/integrations/v1 |
| Keys | srd_live_<prefix>_<secret> | srd_test_<prefix>_<secret> |
| Header on every response | X-Serad-Environment: production | X-Serad-Environment: sandbox |
| Data | Real | Fictitious. They never mix: they are separate databases |
| Who moves visits forward | Salud en Ruta's dispatch centre and units | A simulator |
| Email, notifications, prescriptions, staff alerts | Real | Disabled |
| Invoicing | Real | Nothing is invoiced |
| Cost | As per your contract | Free |
Paths, fields, errors and webhooks are exactly the same in both environments: code that works against the sandbox works against production by changing only the base URL, the key and the webhook secret.
The key determines the environment
Every key belongs to one environment. If you use a key against the wrong environment, the API responds 401 with the type /errors/provider-api/wrong-environment and a message that says so plainly ("Estás usando una clave de pruebas contra producción", that is, you're using a test key against production), so you don't mistake it for a badly copied key:
{
"type": "/errors/provider-api/wrong-environment",
"title": "Clave de otro entorno",
"status": 401,
"detail": "Estás usando una clave de pruebas contra producción",
"instance": "/api/integrations/v1/visits",
"timestamp": "2026-10-01T09:10:00.000Z"
}The X-Serad-Environment header
Every API response, error responses included, carries the X-Serad-Environment header with the value production or sandbox. Use it to:
- Record in your logs which environment each call went to.
- Guard your automated tests: if an integration test gets
production, make it fail immediately. - Show it in your internal screens ("Connected to: sandbox") so nobody creates real visits thinking they're test ones, or the other way round.
curl -sI "https://sandbox-api.saludenruta.es/api/integrations/v1/catalog" -H "X-Api-Key: $SERAD_API_KEY" | grep -i x-serad-environment
# X-Serad-Environment: sandboxIdentifiers differ between environments
The id values of visits, service types and insurers do not match between production and the sandbox: they are separate databases, and each one generates its own. A serviceTypeId copied from the sandbox doesn't exist in production.
So:
- Always use codes, not ids, for catalogue data. When creating a visit, send
serviceTypeCode(for example,GENERAL_CONSULTATION) instead ofserviceTypeId. Codes are the same in both environments. - Read each environment's catalogue when your integration starts (or once a day) and cache it, instead of hard-coding ids in your code or configuration.
- Store visit
idvalues together with the environment in your database, or in separate tables. A sandbox visit can't be retrieved in production. - Link visits to your system through your case number (
externalRef), which is yours and doesn't depend on the environment.
Go-live checklist
Before asking for your production key, check in the sandbox that your integration meets all of the following. We'll go through it with you during validation.
Configuration
- The base URL, the key and the webhook secret are read from the environment's configuration (environment variables or a secrets manager), not hard-coded.
- Your automated tests check that
X-Serad-Environmentissandbox. - The service type is sent with
serviceTypeCode, and the catalogue is read from the API. - The key is only used from your server (never in a browser or an app).
Visits
- You send a unique
Idempotency-Keyfor each new visit and reuse it on retries (idempotency). - You send the case number (
externalRef) if your catalogue saysrequiresExternalRef: true. - You distinguish urgent, standard and scheduled visits, and appointments use
scheduledFromandestimatedMinutes(visits). - You can cancel a visit and you handle the
409when that's no longer possible (cancel).
Webhooks
- You verify the signature of every delivery against the raw body and reject those that don't match or are more than 300 s old (signature).
- You respond with a
2xxwithin 10 s and process afterwards, in the background. - You discard repeated events by their
id(not byX-Serad-Delivery, which changes on a manual redelivery) (receiver idempotency). - Your receiver copes with events arriving out of order (it uses the
milestones, not the order of arrival).
Errors and limits
- You handle errors by their
statusandtype(errors), and you log thedetailand theinstance. - On a
429you wait as long asRetry-Aftersays, and on a5xxyou retry with exponential backoff (rate limits). - An API failure doesn't block your operations: if you can't send an urgent visit, your team knows they can call the dispatch centre (+34 611 673 280).
When everything is green, email us to arrange the validation.
Something missing or not matching what you see? Email our technical team: info@saludenruta.es.
