POST endpoints that dial phones, /v1/calls and /v1/batch-calls, accept an Idempotency-Key header. If your system retries a request after a timeout or a network error, sending the same key guarantees the operation runs only once.
How it works
- We look in the cache for that key within your account.
- Hit with the same body and endpoint → we return the cached response from the first request. We do not fire the call again.
- Hit with a different body, or the same key already used on another endpoint → we answer
409 idempotency_conflict. That is a bug on your side: you are reusing a key for different work. - Miss → we process the request normally. If it succeeds, we cache
(key, body, response)for 24h.
/v1/batch-calls the key is claimed before the batch is sent, so two retries that arrive at the same time cannot launch two batches: the second one gets 409 idempotency_in_progress until the first finishes, and then the cached response. If the first request fails with a 4xx (invalid body, not enough budget), the key is released and you can fix the request and retry with the same key.
When to use it
Firing calls from a workflow
Your n8n or GHL can retry on a timeout. Without idempotency, every retry fires a new call.
Queued messages
If your queue delivers “at-least-once” (SQS, RabbitMQ), the same message can arrive twice.
Webhooks from your CRM
Your CRM may retry failed webhooks, so use the CRM’s own ID as the Idempotency-Key.
Any async code
Wherever retries happen, send the key.
Key format
- 1 to 255 characters.
- Printable ASCII only (
!to~). - Recommended: UUID v4 (
crypto.randomUUID()) or an identifier from your own system (for examplelead-{lead_id}-{deal_id}).
Cache TTL
Keys expire 24 hours after the first successful request. After that, the same key can be used for a new, different operation. If you need duplicate protection beyond 24h, use stable identifiers on your side (for example a uniquewebhook_id in your CRM) and keep your own webhook_id → call_id mapping.
Isolation per customer
Keys are scoped to your account. If you useIdempotency-Key: 12345 and another Ryvo customer uses the same string, there is no collision: each account has its own namespace.
Within your account, all your API keys share that namespace: the same key sent from two different API keys of the same account is the same operation.
Example: integration with backoff
idempotencyKey, even if the first attempt fired the call successfully but the response was lost on the network, the retry returns that same cached response and does NOT fire a second call.