Arbol’s SDKs send each call once and never retry it, so your code decides. Two questions decide it: may a repeat succeed, which the error’s retryable answers, and is a repeat safe, which the operation’s effect answers.
Each operation’s effect
Every operation’s page in the API reference states its Effect:
| Effect | What the operation does | Repeating it |
|---|---|---|
| Reads | Changes nothing. | Safe. |
| Writes | Creates or changes something. | Safe only if the operation is idempotent, or once you’ve confirmed the first attempt didn’t apply. |
| Deletes | Deletes or cancels something. | Same as a write. |
The HTTP method doesn’t tell you the effect. agenda.bookings.lookup is a POST that reads, and agenda.bookings.cancel is a POST that deletes: read the operation’s Effect instead.
When a repeat may help
error.retryable says whether repeating the same request may succeed. These common codes are retryable:
| Status | code |
What to do |
|---|---|---|
429 |
RATE_LIMITED |
Too many requests from this credential. Wait the seconds in Retry-After, then repeat. |
503 |
UPSTREAM_UNAVAILABLE |
Back off, then repeat. |
504 |
UPSTREAM_TIMEOUT |
Back off, then repeat a read. A write may have applied: see below. |
Services add retryable codes of their own, such as PROVIDER_UNAVAILABLE in gMedic, SURA and Visoft. When retryable is false, the same request will fail again: change the input, the credential or the state first.
Writes with an unknown outcome
Some failures don’t say whether a write happened:
502 OUTCOME_UNKNOWN: the operation may have been applied. Check its result before repeating it.- A timeout, or no answer at all. A write that ended with
504 UPSTREAM_TIMEOUT, or whose connection dropped, may have applied too.
Reconcile before you repeat: read the record, or list what the first attempt would have created. Several operations say so in their description, such as gmedic.patients.create (“reconcile an unknown outcome before repeating”), visoft.appointments.create (“Never repeat a booking whose outcome is unknown”) and raiz.channels.create, which isn’t idempotent: list the channels before trying again.
The Rust SDK labels every failure with an outcome: Refused (nothing happened), NotSent (the request never left) or Unknown (a write that may have happened).
Operations that are safe to repeat
Some writes are designed to be repeated:
- Idempotent operations. An operation’s Effect says idempotent when repeating it with the same input has no further effect, and not idempotent when a repeat may act again. Among writes,
raiz.agents.updateandagenda.waitlist.joinare idempotent, whileraiz.contacts.createandsura.appointments.createaren’t. MCP clients see the same as the tool’sidempotentHint. - Idempotency keys.
agenda.appointments.createtakes anidempotencyKey, unique within the organization: reusing it returns the original appointment.sura.appointments.createtakes one too, reused only for the same accepted offer. - Repeats that change nothing. Calling
agenda.waitlist.joinagain for the same patient, service and reason returns the same open entry. Cancelling a Raíz task again withraiz.tasks.cancelchanges nothing. - Version checks. Updates that take
expectedVersionapply only if the record hasn’t changed since you read that version. In Raíz, repeating an update that already applied fails withVERSION_CONFLICTinstead of applying twice.
Pending answers in SURA
A SURA call may answer "status": "pending" with an operationId and retryAfterSeconds instead of its result, while SURA finishes an earlier request. The operation keeps running. After retryAfterSeconds, get the result with sura.operations.get, or repeat the identical call, which returns the same operation instead of starting another.
A retry policy
- Respect
retryable: false. Fix the request before sending it again. The exception isOUTCOME_UNKNOWN: check whether the write applied, and repeat it only if it didn’t. - Repeat reads when
retryableistrue, or when no answer came back. - Repeat writes and deletes only when the operation is idempotent, or once you’ve confirmed the first attempt didn’t apply.
- Wait between attempts. Use
Retry-Afterwhen the answer has it. Otherwise, back off exponentially with jitter, and cap the number of attempts. - Log each attempt’s
X-Request-Id, so a failure can be traced.