Guides
Errors
The error format (RFC 9457), what each HTTP status means and the list of API error types with their cause and fix.
Last reviewed: October 1, 2026
Format
Every error follows the Problem Details standard (RFC 9457), with Content-Type: application/problem+json:
{
"type": "/errors/validation-error",
"title": "Error de validación",
"status": 400,
"detail": "estimatedMinutes must be divisible by 10",
"instance": "/api/integrations/v1/visits",
"errors": [
{ "field": "estimatedMinutes", "message": "estimatedMinutes must be divisible by 10" }
],
"timestamp": "2026-10-01T09:10:00.000Z"
}| Field | Description |
|---|---|
type | Stable identifier of the error. It's a relative path (/errors/…). Your code should decide what to do based on status and type, never on the text. |
title | Short, human-readable summary. Its wording may change. |
status | The same HTTP status code as the response. |
detail | Specific explanation of this case ("El aviso AV-000123 está en estado EN_ROUTE…"). For showing to your operator or logging. |
instance | The path of the request that failed. |
errors | Validation errors only: one item per field, with its path (patient.documentNumber, address.postalCode). |
timestamp | When it happened. |
Some errors include additional fields with data about the case. Ignore any you don't recognise.
The errors[] messages in format validation errors come straight from the validator, in English (must be a UUID, should not exist): they're meant for developers, not end users. Business errors (detail), on the other hand, are worded so they can be shown to users.
When you contact us about an error, include the type, the detail, the instance, the timestamp and the X-Serad-Environment header: with those we can track it down right away.
HTTP status codes
| Code | Meaning | Retry? |
|---|---|---|
200 OK | Success. When creating a visit, it's also the response to a repeat with the same Idempotency-Key. | — |
201 Created | Visit created. | — |
400 Bad Request | The request isn't valid: a malformed field, a service type you don't have, a business rule. | No: fix it. |
401 Unauthorized | The key is missing, invalid, revoked or expired, or belongs to the other environment. | No. |
403 Forbidden | The key is valid but lacks the required scope. | No. |
404 Not Found | The visit (or the report) doesn't exist or isn't yours. Also sandbox paths in production. | No. |
409 Conflict | The current status doesn't allow it (cancelling a visit that is en route) or there's an idempotency conflict. | Only idempotency-in-progress. |
429 Too Many Requests | You've exceeded the rate limit. | Yes, after Retry-After. |
500 Internal Server Error | Our fault. | Yes, with exponential backoff. |
502 / 503 / 504 | The API is temporarily unavailable (deployment, maintenance). | Yes, with exponential backoff. |
Error types
Authentication and rate limits
type | Status | Cause | Fix |
|---|---|---|---|
/errors/provider-api/invalid-key | 401 | There's no X-Api-Key header, or the key doesn't exist, is revoked or expired, or your organisation is inactive. | Check that you're sending the full key in X-Api-Key. If it's correct, email us with its prefix. |
/errors/provider-api/wrong-environment | 401 | Test key against production, or production key against the sandbox. | Use srd_test_… with https://sandbox-api.saludenruta.es/api/integrations/v1 and srd_live_… with https://api.saludenruta.es/api/integrations/v1. See environments. |
/errors/provider-api/scope-missing | 403 | The key doesn't have the scope the endpoint requires (detail says which). | Ask for a key with that scope. See scopes. |
/errors/provider-api/rate-limited | 429 | You've exceeded the key's requests per minute. | Wait Retry-After seconds. Use webhooks instead of polling. |
Idempotency
type | Status | Cause | Fix |
|---|---|---|---|
/errors/provider-api/idempotency-key-invalid | 400 | The Idempotency-Key is longer than 200 characters. | Use a shorter key. |
/errors/provider-api/idempotency-key-reused | 409 | You've used the same Idempotency-Key with a different body in the last 24 h. | Generate one key per visit; if it's a retry, send exactly the same body. |
/errors/provider-api/idempotency-in-progress | 409 | The first request with that key is still being processed. | Wait a few seconds and retry with the same key. |
Creating visits
type | Status | Cause | Fix |
|---|---|---|---|
/errors/validation-error | 400 | One or more fields have the wrong format (missing required field, length, enum…) or there are fields the API doesn't recognise (property … should not exist). errors[] says which. | Fix the fields listed. See request fields. |
/errors/provider-api/service-type-required | 400 | You sent neither serviceTypeCode nor serviceTypeId. | Send serviceTypeCode (recommended). |
/errors/provider/service-type-not-allowed | 400 | That service type isn't in your catalogue (it has no price in your current rate card) or the code doesn't exist. | Read your catalogue and use one of its code values. |
/errors/provider/insurer-not-managed | 400 | The insurerId isn't one of your insurers, or you aren't an intermediary. | Use an id from catalog.insurers in the same environment, or don't send insurerId. |
/errors/visits/external-ref-required | 400 | Your contract requires a case number and you didn't send one. | Send externalRef. |
/errors/visits/invalid-schedule | 400 | The appointment slot is inconsistent (an end without a start, or an end before the start). | Send scheduledFrom and estimatedMinutes, without scheduledTo. |
/errors/visits/schedule-in-past | 400 | The appointment starts more than 15 minutes in the past. | Check the date and time zone of scheduledFrom. |
/errors/visits/schedule-beyond-horizon | 400 | The appointment is more than 90 days ahead. | Schedule it closer, or send it later. |
/errors/visits/provider-inactive | 400 | Your organisation is inactive and can't create new visits. | Contact us. |
/errors/patients/document-number-required | 400 | You set documentType (DNI, NIE or passport) without documentNumber. | Send the number, or documentType: "NONE". |
/errors/patients/document-not-allowed | 400 | You sent documentNumber with documentType: "NONE". | Set the correct document type. |
/errors/patients/document-invalid | 400 | The DNI or NIE has an invalid format or its check letter doesn't match. | Double-check the document with the patient. |
/errors/patients/not-found | 404 | The patientId doesn't exist or isn't from one of your visits. | Send the patient's details in patient. |
/errors/dates/in-future | 400 | A date that can't be in the future is (e.g. birthDate, or dateFrom in the list). | Fix the date. |
/errors/dates/range-invalid | 400 | In the list, dateTo is earlier than dateFrom. | Fix the range. |
Retrieval, cancellation and reports
type | Status | Cause | Fix |
|---|---|---|---|
/errors/visits/not-found | 404 | The visit doesn't exist, belongs to another provider or is in the other environment. | Check the id and the environment. You can look it up by externalRef. |
/errors/validation-error | 400 | The id in the path isn't a UUID. | Use the visit's id, not its code. |
/errors/provider/visit-not-cancellable | 409 | The visit is no longer PENDING or ASSIGNED (the unit is en route or on site, or the visit has already finished or been cancelled). | Call the dispatch centre (+34 611 673 280). See cancel. |
/errors/provider/report-not-available | 404 | There's no signed and shared report, or the patient hasn't consented. | Wait for the report.shared webhook. Without consent there will be no report. |
Sandbox
type | Status | Cause | Fix |
|---|---|---|---|
/errors/sandbox/only-in-sandbox | 404 | You called /sandbox/… in production. | Those paths only exist at https://sandbox-api.saludenruta.es/api/integrations/v1. |
/errors/sandbox/visit-not-advanceable | 400 | You asked to move a visit to an earlier or disallowed status (e.g. from EN_ROUTE to ASSIGNED). | See manual simulation. |
Server errors
type | Status | Cause | Fix |
|---|---|---|---|
/errors/internal-error | 500 | Unexpected error on our side. | Retry with exponential backoff. If it persists, email us with the timestamp and the instance. |
/errors/rate-limited | 429 | Platform-wide limit (separate from your key's limit). | Same as provider-api/rate-limited: wait for Retry-After. |
Something missing or not matching what you see? Email our technical team: info@saludenruta.es.
