POST /v1/chats/{chat_id}/share-contact-card refuses a repeat once the card’s message exists. Every other operation ignores the header. Read this page before you write a retry.
The key travels in the Idempotency-Key request header. X-Idempotency-Key is read when the first is absent. The key is a string of your choice: use a random UUID.
Operations that honor the key
POST /v1/chats/{chat_id}/share-contact-card reads the key and never replays. Once a request has created the card’s message, a repeat with the same key returns 409 idempotency_conflict. When no message was created, the repeat sends the card. See Contact cards.
Lines and hosted macOS sessions
POST /v1/lines/assign, POST /v1/lines/shared and POST /v1/macos-sessions/rent each take or create something billable. Without a key, every retry takes one more line or rents one more session. Always send a key to these three operations.
- Replay. A request repeated with the same key, the same method, the same path and the same body within 24 hours returns the first successful response again. The body is the first one, with the
trace_idand therequest_idof the first request. TheX-Request-Idheader belongs to the new request, so it differs fromrequest_idin the body. No header marks a replay. - Same body. The body is compared byte for byte. A request with no body and a request with the body
{}are two different requests. - Collision. The same key with another method, path or body returns
409 idempotency_key_collision. - In flight. While the first request is still running, a repeat returns
503 idempotency_in_flight: retry with the same key. A request that was abandoned midway frees its key after 60 seconds. - Failures. Only a
2xxresponse is recorded. A request that failed leaves no trace: a retry with the same key runs again. - Lifetime. The key is remembered for 24 hours from the request that succeeded. After that, the same request with the same key runs again: it takes or creates another line, or rents another session.
- Length. A key longer than 255 characters returns
400 validation_failed, with an issue onidempotency-keyand the codetoo_big.
POST /v1/lines/assign with as_test_line: true assigns the line first and converts it second. When the conversion alone fails, the response is the error, the line stays assigned to you as a rented line, and the key is not recorded: a retry with the same key assigns another line. Before you retry, read your lines with GET /v1/lines/my.
Messages, reactions and contact cards
POST /v1/messages and the two reaction operations recognize a request repeated with the same key on the same line and with the same content. The key belongs to the line: on a send, the line that sends the message, and on a reaction, the line of the message.
- Replay. A repeated send returns
202with the message already created, in its state at the time of the repeat:statuscan have moved tosentordelivered, andqueue_infoisnull. A repeatedPOSTof a reaction returns the entry of the first call as it is now, and does not add the reaction again: after a laterDELETE, the repeat returns that entry although the reaction is removed. Read thereactionsfield of the message to know what is in place. When the first call added the reaction, the repeat returns409 idempotency_conflictonce a later call has changed its type. When the first call changed the type of an existing reaction, or found that type in place, iSnap does not record its key: a repeat after a later change of type changes the reaction again. A repeatedDELETEreturns the response of the first call. Thetrace_idand therequest_idare new. Apart from that case, nothing is sent a second time. - Checks before the key. On a send, the checks on the line, and with a trial key the checks on the trial, run before the key is read. A repeat returns
400 line_not_activeor403 line_suspendedonce the line is no longeractive(403 line_not_activewith a trial key), and402 trial_needs_inboundonce the trial isdormant, which the last send a trial accepts makes it. Such a refusal does not mean that the first request was refused: look for the message withGET /v1/messages. - Same content. The content is compared after parsing: the order of the JSON fields does not matter.
- Collision. The same key on the same line with different content returns
409 idempotency_conflict. The same key with anotherline_idis a separate request: nothing is compared, and a new message is created. A reaction and a send share the keys of their line. APOST /v1/messageswith the key of a reaction is not refused: it returns202with the reaction entry and sends nothing. Use a new key for every action. - Lifetime. On a send, the key is remembered as long as its message exists: at least 72 hours from the creation of the message. Once the message is deleted, the same key creates a new message. On a reaction, the key is remembered at most until the message reacted to is deleted: the reaction entry is deleted with that message, which can be less than 72 hours after the reaction. The reaction call then returns
404 not_found, and the key can be used again on the line. - One at a time. Send retries one after the other, not in parallel, and keep the key at 255 characters or fewer. Two simultaneous requests with one new key, or a longer key, can end in
500 internal_error. Two requests with one key never create two messages: one of them can end in500 internal_error, and a retry with the same key then returns the message. - Trial keys. With a trial key, the key belongs to the trial’s pool line, which other trials can share. Use keys that are unique everywhere, such as random UUIDs.
POST /v1/chats/{chat_id}/share-contact-card with the same key is not replayed: it returns 409 idempotency_conflict once the card’s message exists, and sends the card when no message was created. The key belongs to the line of the chat, and is remembered as long as the card’s message exists, at least 72 hours from its creation: once that message is deleted, a repeat sends the card. Send a key with every call: without a key, every repeat sends the card. A key that a reaction already used on the chat’s line is not refused: the call returns 202 with the id of the reaction entry in message_id, and sends nothing. Use a new key for every call. When you did not receive the response of this call, look for the message with GET /v1/messages before you resend the card.
Attachments
OnPOST /v1/attachments, a repeat with the same key returns the same attachment id with a new upload_url and a new upload deadline, as long as the first attachment is still awaiting_upload and its deadline has not passed. The deadline is 15 minutes after the call, and each repeat that returns the attachment sets a new one. The body of the repeat is not compared with the first. In any other state, the key returns 409 idempotency_terminal_state: create the attachment again with a new key. The key is never forgotten: once it has created an attachment, it never creates another one, and the 409 has no time limit. See Attachments.
Retrying safely
Retry a write only on an operation that honorsIdempotency-Key, with the key of the first attempt.
- Generate the key before the first attempt, one per action, and store it with your own record of the action: the message to send, the line to assign.
- Send the request. When the response does not arrive, or is a
500or a503, resend the same request with the same key, up to 2 times, with exponential backoff. - Send retries one after the other. Never send two requests with the same key at the same time.
- To send a new message after a
failedone, use a new key: iSnap never resends a failed message, and the old key returns the failed message. - Retry while the key is remembered: within 24 hours of the first attempt on the three line and session operations, within 72 hours on a send, and while the message reacted to exists on a reaction. Later, the key can be forgotten, and the same request to a line or session operation, or the same send, then runs again: see the row “How long the key is remembered” of Operations that honor the key.
Operations that ignore the key
Every operation that Operations that honor the key does not list ignores the header. A repeated request acts again:
When you did not receive the response of one of these writes, do not resend it blindly. Read the resource first: list your webhook subscriptions, your pre-orders or your lines, and resend the write only when the first one did not run.
A repeat of a write that already succeeded can return an error instead of acting twice: a second cancel of a message returns
409 too_late_to_cancel, a second withdrawal of a variant change 404 no_pending_variant_change, a second release 404 not_found, and a second cancel of a pre-order 400 pre_order_already_confirmed. Read the resource before you report a failure.
Some writes are safe to repeat because the second request finds the work done. POST /v1/lines/{id}/terminate on a line that is already cancelled returns 200 with the line unchanged. POST /v1/lines/{id}/cancel on a line that already has a cancel_at returns the same cancel_at. The guide of each operation says so where it holds.
By default, the SDK adds an Idempotency-Key to every write. It retries every method, the ones of this section included. Pass { maxRetries: 0 } to a method whose operation ignores the key: see SDK errors. A client created with idempotencyKey: "manual" sends a key only when the call passes one, and still retries.