---
title: "Reintentos"
description: "Qué llamadas es seguro repetir, cuándo puede servir una repetición y qué hacer cuando no se sabe el resultado de una escritura."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.getarbol.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Reintentos

Los SDK de Arbol envían cada llamada una vez y nunca la reintentan, así que tu código decide. Lo deciden dos preguntas: si una repetición puede tener éxito (lo responde el `retryable` del error) y si una repetición es segura (lo responde el efecto de la operación).

## El efecto de cada operación

La página de cada operación en la referencia de la API indica su efecto (**Effect**):

| Efecto | Qué hace la operación | Repetirla |
| --- | --- | --- |
| Lee | No cambia nada. | Es seguro. |
| Escribe | Crea o cambia algo. | Es seguro solo si la operación es idempotente, o una vez que confirmaste que el primer intento no se aplicó. |
| Elimina | Elimina o cancela algo. | Igual que una escritura. |

El método HTTP no te dice el efecto. [`agenda.bookings.lookup`](/api/agenda/bookings/lookup/) es un `POST` que lee, y [`agenda.bookings.cancel`](/api/agenda/bookings/cancel/) es un `POST` que elimina: lee en cambio el efecto de la operación.

## Cuándo puede servir repetir

`error.retryable` dice si repetir la misma solicitud puede tener éxito. Estos códigos comunes son reintentables:

| Estado | `code` | Qué hacer |
| --- | --- | --- |
| `429` | `RATE_LIMITED` | Demasiadas solicitudes de esta credencial. Espera los segundos de `Retry-After` y luego repite. |
| `503` | `UPSTREAM_UNAVAILABLE` | Aplica backoff y luego repite. |
| `504` | `UPSTREAM_TIMEOUT` | Aplica backoff y luego repite una lectura. Una escritura puede haberse aplicado: consulta más abajo. |

Los servicios agregan sus propios códigos reintentables, como `PROVIDER_UNAVAILABLE` en gMedic, SURA y Visoft. Cuando `retryable` es `false`, la misma solicitud volverá a fallar: primero cambia la entrada, la credencial o el estado.

## Escrituras con resultado desconocido

Algunos fallos no dicen si una escritura ocurrió:

- **`502 OUTCOME_UNKNOWN`**: puede que la operación se haya aplicado. Revisa su resultado antes de repetirla.
- **Un timeout, o ninguna respuesta.** Una escritura que terminó con `504 UPSTREAM_TIMEOUT`, o cuya conexión se cayó, también puede haberse aplicado.

Concilia antes de repetir: lee el registro o lista lo que el primer intento habría creado. Varias operaciones lo dicen en su descripción, como [`gmedic.patients.create`](/api/gmedic/patients/create/) ("reconcile an unknown outcome before repeating"), [`visoft.appointments.create`](/api/visoft/appointments/create/) ("Never repeat a booking whose outcome is unknown") y [`raiz.channels.create`](/api/raiz/channels/create/), que no es idempotente: lista los canales antes de intentarlo de nuevo.

El SDK de Rust etiqueta cada fallo con un `outcome`: `Refused` (no pasó nada), `NotSent` (la solicitud nunca salió) o `Unknown` (una escritura que puede haber ocurrido).

## Operaciones que es seguro repetir

Algunas escrituras están diseñadas para repetirse:

- **Operaciones idempotentes.** El efecto de una operación dice *idempotent* cuando repetirla con la misma entrada no tiene ningún efecto adicional, y *not idempotent* cuando una repetición puede volver a actuar. Entre las escrituras, [`raiz.agents.update`](/api/raiz/agents/update/) y [`agenda.waitlist.join`](/api/agenda/waitlist/join/) son idempotentes, mientras que [`raiz.contacts.create`](/api/raiz/contacts/create/) y [`sura.appointments.create`](/api/sura/appointments/create/) no lo son. Los clientes MCP ven lo mismo en el `idempotentHint` de la herramienta.
- **Claves de idempotencia.** [`agenda.appointments.create`](/api/agenda/appointments/create/) recibe un `idempotencyKey`, único dentro de la organización: reutilizarlo devuelve la cita original. [`sura.appointments.create`](/api/sura/appointments/create/) también recibe uno, que se reutiliza solo para la misma oferta aceptada.
- **Repeticiones que no cambian nada.** Llamar otra vez a [`agenda.waitlist.join`](/api/agenda/waitlist/join/) para el mismo paciente, servicio y motivo devuelve la misma entrada abierta. Cancelar otra vez una tarea de Raíz con [`raiz.tasks.cancel`](/api/raiz/tasks/cancel/) no cambia nada.
- **Validación de versión.** Las actualizaciones que reciben `expectedVersion` se aplican solo si el registro no ha cambiado desde que leíste esa versión. En Raíz, repetir una actualización que ya se aplicó falla con `VERSION_CONFLICT` en lugar de aplicarse dos veces.

## Respuestas pendientes en SURA

Una llamada a SURA puede responder `"status": "pending"` con un `operationId` y `retryAfterSeconds` en lugar de su resultado, mientras SURA termina una solicitud anterior. La operación sigue en curso. Después de `retryAfterSeconds`, obtén el resultado con [`sura.operations.get`](/api/sura/operations/get/), o repite la llamada idéntica, que devuelve la misma operación en lugar de iniciar otra.

## Una política de reintentos

1. **Respeta `retryable: false`.** Corrige la solicitud antes de enviarla de nuevo. La excepción es `OUTCOME_UNKNOWN`: revisa si la escritura se aplicó y repítela solo si no se aplicó.
2. **Repite las lecturas** cuando `retryable` sea `true`, o cuando no haya llegado ninguna respuesta.
3. **Repite escrituras y eliminaciones** solo cuando la operación sea idempotente, o una vez que confirmaste que el primer intento no se aplicó.
4. **Espera entre intentos.** Usa `Retry-After` cuando la respuesta lo traiga. Si no, aplica backoff exponencial con jitter y limita el número de intentos.
5. **Registra el `X-Request-Id` de cada intento**, para poder rastrear un fallo.

Source: https://docs.getarbol.com/developers/concepts/retries/index.mdx
