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

# Message send retries

> Retry a send safely and recover its original message identifier

Use one `Idempotency-Key` for each intended email, SMS/MMS, or iMessage. Reuse that
key and the same input if the request times out. Use a new key for a deliberately
new message, even if its content is identical.

For raw HTTP requests, also send `Prefer: idempotency-replay` to select the replay
contract below. SDKs and CLI version 0.7.10 send this preference automatically.
Requests without this preference keep their existing behavior: completed keyed
email retries return `409`, and SMS/iMessage calls are independent sends.
Email and SMS still wait for send acceptance before returning success. iMessage
keeps its existing queued acknowledgement. Response shapes and delivery fields are unchanged.

The SDKs generate a key for each send call and preserve it during bounded request
retries. Pass an explicit key for a workflow that retries across separate calls or
processes. The CLI exposes `--idempotency-key` on email, text, and iMessage sends.

## Retry outcomes

| Situation | Response |
| :- | :- |
| Original response is available | Original HTTP status and response body, including the same message ID |
| Same key with different input | `409 idempotency_key_reused` |
| Original request is still running | `409 idempotency_in_progress`; respect `Retry-After` |
| Send outcome is not confirmed | `503 send_outcome_ambiguous`; check the existing message rather than submitting a new one |
| Original response is unavailable | `409 result_unavailable`; use lookup or message history |

Recorded responses include `Preference-Applied: idempotency-replay`.
`Idempotency-Rejected: true` means the original request was definitively rejected
without sending. After correcting that issue, you may explicitly start a new
attempt with a new key. Do not infer this from HTTP status alone: a conflict or
missing result can refer to a message that was already sent.
This includes recorded pre-send iMessage rejections. Authentication or validation
failures that cannot establish the original request's outcome may omit the header.

Keys must be nonblank, contain 1–255 printable ASCII characters, and have a seven-day supported
response-replay window. Do not intentionally reuse a key for another message.
Keys are scoped to your organization, sending resource, and operation.
An expired key cannot start a new message; its original response is unavailable.

A replay is the original request result, not current delivery tracking. Use the
message's normal get endpoint or delivery webhooks for live status. A successful
queued response does not mean the recipient has received the message. Request
retries do not guarantee delivery retries. A missing delivery receipt does not
justify creating a second message.

For `503 send_outcome_ambiguous`, respect `Retry-After` before checking again with
the same key. Lookup or outbound history may help, but an interrupted email/SMS
request may have no recoverable message ID and can remain unresolved. Never
automatically switch keys because a lookup returned `404`.

## Recover the original message ID

```text theme={null}
GET /api/v1/message-sends/lookup
```

| Query parameter | Description |
| :- | :- |
| `sender_kind` | `mailbox`, `phone_number`, or `imessage_identity` |
| `sender_id` | Mailbox UUID for `mailbox` (not its email address), phone-number UUID for `phone_number`, or agent-identity UUID for `imessage_identity` |
| `operation` | `mail.send`, `mail.reply_all`, `mail.forward`, `text.send`, or `imessage.send` |

Supply the original key in the `Idempotency-Key` header, together with your usual
authentication. This read-only endpoint does not send or resume a message.

```json theme={null}
{"message_id":"10000000-0000-0000-0000-000000000001"}
```

`404` means no result is available to you. It does not prove the message was never
sent: the original request may still be processing. Do not generate a new key
automatically after a missing lookup result.

Lookup can return `403` when your credential is not allowed to use the selected
channel, or `422` for a missing/invalid key, UUID, sender kind, or operation.
An email-address helper resolves the mailbox UUID before performing lookup.

An interrupted email or SMS send may remain unconfirmed without a message ID.
Reusing its key does not submit another message. After a confirmed rejection that
permits retry, the eventual message ID may differ and the email may use your updated mailbox signature
from the earlier unsuccessful attempt. Once a final response is available, replays
return that original response.

## Keep the key after an error

Python exposes `get_message_request_key(error)` from `inkbox`; TypeScript exposes
`getMessageRequestKey(error)` from `@inkbox/sdk`. These recover the generated key
from send errors, including transport failures. The CLI prints it in stderr and
includes `error.idempotencyKey` with `--json`.

Rust errors do not expose generated keys. Preserve your own key and use an
explicit-key send method when you need to retry or look up the request across calls.

Use `imessages.get(message_id)` in Python, `imessages.get(messageId)` in TypeScript,
or `inkbox imessage get <message-id> --identity <handle>` to read a recovered iMessage.

<CodeGroup>
  ```python Python theme={null}
  message_id = inkbox.message_sends.lookup_email(
      "my-agent@inkboxmail.com",
      operation="mail.send",
      idempotency_key="order-123-confirmation",
  )
  ```

  ```typescript TypeScript theme={null}
  const messageId = await inkbox.messageSends.lookupEmail(
    "my-agent@inkboxmail.com",
    { operation: "mail.send", idempotencyKey: "order-123-confirmation" },
  );
  ```

  ```bash CLI theme={null}
  inkbox send-lookup --sender-kind mailbox \
    --email-address my-agent@inkboxmail.com --operation mail.send \
    --idempotency-key order-123-confirmation
  ```
</CodeGroup>
