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

# Retry a failed message

> Sends a failed outbound message again. Only direct-chat messages that failed before they left Blujay can be retried. Group messages, and messages that may already have reached the recipient, return `409 RETRY_NOT_ALLOWED`. `RETRY_NOT_ALLOWED` means Blujay can't safely send this message again, often because it may already have reached the recipient. Don't automatically send a replacement. Check the message's status and the conversation first, for example for a reply. Send a new message only when you're sure the first one didn't arrive, or when a possible duplicate is acceptable.

Inbound messages return `400 INVALID_MESSAGE`. Send an `Idempotency-Key` so you can safely retry this call.




## OpenAPI

````yaml /api-reference/openapi.yaml post /v1/messages/{message_id}/retry
openapi: 3.1.0
info:
  title: Blujay API
  version: 1.0.0
  description: >-
    The Blujay API lets you send and receive iMessage, RCS and SMS messages from
    your

    Blujay phone numbers, manage contacts, and receive real-time webhooks.


    Every response uses the same JSON envelope and carries a `trace_id` you can
    share

    with support. Authenticate with your workspace API key.
servers:
  - url: https://api.blujay.io
    description: Production
security:
  - PublicApiKey: []
tags:
  - name: Auth
    description: Check which workspace and credential a request is using.
  - name: Chats
    description: >-
      Direct and group conversations between your Blujay numbers and your
      contacts.
  - name: Messages
    description: Send, read, edit, unsend and retry messages.
  - name: Phone Numbers
    description: >-
      Your Blujay numbers, whether each can send right now, and their live
      sending limits.
  - name: Capability
    description: >-
      Check whether a phone number can be reached by iMessage, RCS or SMS before
      you send.
  - name: Contacts
    description: Create, find, update, tag, assign and opt out contacts.
  - name: vCards
    description: >-
      Saved contact cards that you can send in a message. Recipients get a
      native contact card over iMessage, SMS or RCS.
  - name: Team Members
    description: People in your workspace you can assign contacts to.
  - name: Workflows
    description: Enroll contacts in workflows you start manually.
  - name: Attachments
    description: >-
      Upload files once and reuse them in messages, or download files from
      messages.
  - name: Webhooks
    description: Subscribe to events, inspect delivery attempts and verify signatures.
paths:
  /v1/messages/{message_id}/retry:
    post:
      tags:
        - Messages
      summary: Retry a failed message
      description: >
        Sends a failed outbound message again. Only direct-chat messages that
        failed before they left Blujay can be retried. Group messages, and
        messages that may already have reached the recipient, return `409
        RETRY_NOT_ALLOWED`. `RETRY_NOT_ALLOWED` means Blujay can't safely send
        this message again, often because it may already have reached the
        recipient. Don't automatically send a replacement. Check the message's
        status and the conversation first, for example for a reply. Send a new
        message only when you're sure the first one didn't arrive, or when a
        possible duplicate is acceptable.


        Inbound messages return `400 INVALID_MESSAGE`. Send an `Idempotency-Key`
        so you can safely retry this call.
      operationId: retryPublicMessage
      parameters:
        - $ref: '#/components/parameters/MessageId'
        - $ref: '#/components/parameters/IdempotencyKey'
      responses:
        '200':
          description: >-
            This `Idempotency-Key` was already used for the same request. The
            response is the original one.
          headers:
            X-Trace-ID:
              $ref: '#/components/headers/XTraceID'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    required:
                      - data
                    properties:
                      data:
                        $ref: '#/components/schemas/AcceptedMessage'
        '202':
          description: The retry was accepted.
          headers:
            X-Trace-ID:
              $ref: '#/components/headers/XTraceID'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/SuccessEnvelope'
                  - type: object
                    required:
                      - data
                    properties:
                      data:
                        $ref: '#/components/schemas/AcceptedMessage'
        '400':
          $ref: '#/components/responses/ValidationError'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/RetryRejected'
        '503':
          $ref: '#/components/responses/QueueUnavailable'
      security:
        - PublicApiKey: []
components:
  parameters:
    MessageId:
      name: message_id
      in: path
      required: true
      schema:
        $ref: '#/components/schemas/MessageId'
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: false
      schema:
        type: string
        minLength: 1
        maxLength: 255
      description: >
        A unique value you choose (1–255 characters) that makes this request
        safe to retry. Send the same key with the same request and Blujay
        returns the original response instead of doing the work again. Use a new
        key for each new action.


        Blujay keeps keys for at least 24 hours. Keys are separate for each API
        key and each route family (for example every `POST
        /v1/chats/{chat_id}/messages` shares one). Reusing a key with a
        different request returns `409 IDEMPOTENCY_REQUEST_MISMATCH`. Sending it
        while the first request is still running returns `409
        IDEMPOTENCY_IN_PROGRESS`; retry shortly.
  headers:
    XTraceID:
      description: >-
        Unique ID for this request. It matches `trace_id` in the body. Include
        it when you contact Blujay support.
      schema:
        $ref: '#/components/schemas/TraceId'
    RetryAfter:
      description: Seconds to wait before retrying.
      schema:
        type: integer
        minimum: 1
  schemas:
    SuccessEnvelope:
      type: object
      description: Wraps every successful response. The result is in `data`.
      required:
        - status
        - trace_id
        - timestamp
      properties:
        status:
          type: string
          enum:
            - success
        trace_id:
          $ref: '#/components/schemas/TraceId'
        timestamp:
          type: string
          examples:
            - '2026-10-14T17:01:00.000Z'
          format: date-time
        client_request_id:
          type: string
          description: >-
            Your `X-Request-ID` header value, echoed back when you send one. Not
            included on the send responses (`POST /v1/chats`, `POST
            /v1/chats/{chat_id}/messages`, `POST
            /v1/messages/{message_id}/retry`); keep your own mapping from your
            request ID to the returned message ID there.
    AcceptedMessage:
      type: object
      required:
        - chat_id
        - message
      properties:
        chat_id:
          $ref: '#/components/schemas/ChatId'
        message:
          type: object
          additionalProperties: false
          required:
            - id
            - direction
            - delivery_status
            - service
            - from
            - to
            - created_at
            - parts
          properties:
            id:
              $ref: '#/components/schemas/MessageId'
            direction:
              type: string
              enum:
                - outbound
            delivery_status:
              type: string
              enum:
                - queued
            preferred_service:
              type:
                - string
                - 'null'
              enum:
                - iMessage
                - RCS
                - SMS
                - null
              description: The service you asked for, or null for Auto.
            service:
              type:
                - string
                - 'null'
              enum:
                - iMessage
                - RCS
                - SMS
                - null
              description: >-
                The service that actually carried the message, once confirmed.
                Null until then.
            from:
              type: string
              examples:
                - '+15555550100'
              description: Your Blujay number that sends this message.
            to:
              type: array
              items:
                type: string
                examples:
                  - '+15555550123'
              description: The recipients' phone numbers.
            created_at:
              type: string
              examples:
                - '2026-10-14T17:01:00.000Z'
              format: date-time
            parts:
              type: array
              items:
                $ref: '#/components/schemas/MessageOutputPart'
            reply_to:
              $ref: '#/components/schemas/MessageReplyToInfo'
              description: Present only when the request included `reply_to`.
    MessageId:
      type: string
      examples:
        - msg_abc123
      pattern: ^msg_[A-Za-z0-9_-]+$
    TraceId:
      type: string
      examples:
        - trc_8f2a1c0e4b7d
      pattern: ^trc_[A-Za-z0-9_-]+$
    ChatId:
      type: string
      examples:
        - chat_abc123
      pattern: ^chat_[A-Za-z0-9_-]+$
    MessageOutputPart:
      oneOf:
        - $ref: '#/components/schemas/TextPart'
        - $ref: '#/components/schemas/LinkPart'
        - $ref: '#/components/schemas/MediaOutputPart'
      discriminator:
        propertyName: type
    MessageReplyToInfo:
      type: object
      additionalProperties: false
      required:
        - message_id
        - part_index
        - thread_root_message_id
      description: >-
        What a message replies to. For a reply sent through Blujay, `message_id`
        is the message it named. For a reply that arrived over iMessage, Apple
        reports only the start of the thread, so `message_id` equals
        `thread_root_message_id`. IDs are null when Blujay doesn't have that
        message (for example, it was sent before your history was imported).
      properties:
        message_id:
          oneOf:
            - $ref: '#/components/schemas/MessageId'
            - type: 'null'
        part_index:
          type: integer
          examples:
            - 0
          minimum: 0
          description: Which part of the replied-to message the reply targets (0-based).
        thread_root_message_id:
          oneOf:
            - $ref: '#/components/schemas/MessageId'
            - type: 'null'
          description: >-
            The message that started the thread. Use it to group a thread
            without an extra call, or page through the thread with `GET
            /v1/messages/{message_id}/thread`.
    ErrorEnvelope:
      type: object
      description: >-
        Wraps every error response. Branch on `error.code`, and include
        `trace_id` when you contact support.
      required:
        - status
        - error
        - trace_id
        - timestamp
      properties:
        status:
          const: error
        error:
          $ref: '#/components/schemas/PublicError'
        trace_id:
          $ref: '#/components/schemas/TraceId'
        timestamp:
          type: string
          examples:
            - '2026-10-14T17:01:00.000Z'
          format: date-time
        client_request_id:
          type: string
          description: Your `X-Request-ID` header value, echoed back when you send one.
    TextPart:
      type: object
      required:
        - type
        - text
      properties:
        type:
          const: text
        text:
          type: string
          examples:
            - Hi Ava, this is Sam from the Northwind support team.
          minLength: 1
          maxLength: 10000
        text_decorations:
          type: array
          maxItems: 100
          description: >-
            Bold, italic, strikethrough or underline styling for ranges of the
            text. Styles work in text-only follow-up messages in direct iMessage
            chats. Animations, styles on a first message, and styles in other
            chats return `FEATURE_NOT_ENABLED`.
          items:
            $ref: '#/components/schemas/TextDecoration'
    LinkPart:
      type: object
      required:
        - type
        - value
      description: >-
        A link that recipients see as a rich preview. It must be the only part
        in the message, and it can't be in a chat's first message.
      properties:
        type:
          const: link
        value:
          type: string
          examples:
            - https://example.com/schedule
          format: uri
          pattern: ^https://
          maxLength: 2048
    MediaOutputPart:
      type: object
      additionalProperties: false
      required:
        - type
        - attachment_id
      properties:
        type:
          const: media
        attachment_id:
          $ref: '#/components/schemas/AttachmentId'
        filename:
          type: string
          examples:
            - receipt.jpg
        content_type:
          type: string
          examples:
            - image/jpeg
        size_bytes:
          type: integer
          examples:
            - 284512
          minimum: 1
        download_url:
          type: string
          examples:
            - /v1/attachments/att_abc123/content
          pattern: ^/v1/attachments/att_.+/content$
          description: >-
            Path to download the file with your API credentials. Present once
            the file is ready.
    PublicError:
      type: object
      required:
        - code
        - message
        - type
        - http_status
        - retryable
        - doc_url
        - details
      properties:
        code:
          type: string
          description: Stable, machine-readable error code. Branch on this value.
          examples:
            - RATE_LIMIT_EXCEEDED
            - QUEUE_UNAVAILABLE
            - FEATURE_NOT_ENABLED
        message:
          type: string
          description: Human-readable explanation. It can change, so don't parse it.
        type:
          type: string
          description: Broad error category.
          examples:
            - validation
            - auth
            - idempotency
            - rate_limit
            - queue
            - feature_gate
        http_status:
          type: integer
          description: The HTTP status code of the response.
          enum:
            - 400
            - 401
            - 403
            - 404
            - 409
            - 413
            - 415
            - 422
            - 429
            - 500
            - 502
            - 503
        retryable:
          type: boolean
          description: Whether sending the same request again later can succeed.
        doc_url:
          type: string
          format: uri
          description: Link to documentation for this error code.
        details:
          type: array
          description: >-
            Extra structured context, such as the fields that failed validation
            or the limit you hit.
          items:
            type: object
            additionalProperties: true
        retry_after:
          type: integer
          minimum: 1
          description: >-
            Seconds to wait before retrying. Also sent as the `Retry-After`
            header.
        reset_at:
          type: string
          examples:
            - '2026-10-14T17:01:00.000Z'
          format: date-time
          description: When the limit you hit resets.
    TextDecoration:
      type: object
      required:
        - range
        - style
      description: >-
        A text style applied to a range of the text, measured in UTF-16 code
        units.
      properties:
        range:
          type: array
          description: Character range `[start, end)` in UTF-16 code units.
          minItems: 2
          maxItems: 2
          prefixItems:
            - type: integer
              minimum: 0
            - type: integer
              minimum: 0
        style:
          type: string
          enum:
            - bold
            - italic
            - strikethrough
            - underline
    AttachmentId:
      type: string
      examples:
        - att_abc123
      pattern: ^att_[A-Za-z0-9_-]+$
  responses:
    ValidationError:
      description: The request is invalid. `details` lists the fields that failed.
      headers:
        X-Trace-ID:
          $ref: '#/components/headers/XTraceID'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            invalidInput:
              value:
                status: error
                error:
                  code: VALIDATION_ERROR
                  message: Request validation failed
                  type: validation
                  http_status: 400
                  retryable: false
                  doc_url: https://docs.blujay.io/errors/validation-error
                  details: []
                trace_id: trc_8f2a1c0e4b7d
                timestamp: '2026-10-14T00:00:00.000Z'
    NotFound:
      description: The resource doesn't exist or isn't in your workspace.
      headers:
        X-Trace-ID:
          $ref: '#/components/headers/XTraceID'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    RetryRejected:
      description: >-
        The message can't be retried (`RETRY_NOT_ALLOWED`), or the
        `Idempotency-Key` conflicts with an earlier request.
      headers:
        X-Trace-ID:
          $ref: '#/components/headers/XTraceID'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            retryNotAllowed:
              value:
                status: error
                error:
                  code: RETRY_NOT_ALLOWED
                  message: Only failed messages can be retried
                  type: validation
                  http_status: 409
                  retryable: false
                  doc_url: https://docs.blujay.io/errors/retry-not-allowed
                  details: []
                trace_id: trc_8f2a1c0e4b7d
                timestamp: '2026-10-14T00:00:00.000Z'
            idempotencyInProgress:
              value:
                status: error
                error:
                  code: IDEMPOTENCY_IN_PROGRESS
                  message: An idempotent request with this key is already in progress
                  type: idempotency
                  http_status: 409
                  retryable: true
                  doc_url: https://docs.blujay.io/errors/idempotency-in-progress
                  details: []
                trace_id: trc_8f2a1c0e4b7d
                timestamp: '2026-10-14T00:00:00.000Z'
            idempotencyMismatch:
              value:
                status: error
                error:
                  code: IDEMPOTENCY_REQUEST_MISMATCH
                  message: Idempotency key was reused with a different request shape
                  type: idempotency
                  http_status: 409
                  retryable: false
                  doc_url: https://docs.blujay.io/errors/idempotency-request-mismatch
                  details: []
                trace_id: trc_8f2a1c0e4b7d
                timestamp: '2026-10-14T00:00:00.000Z'
    QueueUnavailable:
      description: >-
        Blujay can't accept the request right now (`QUEUE_UNAVAILABLE`). Retry
        after `Retry-After` seconds; for sends, reuse the same
        `Idempotency-Key`.
      headers:
        X-Trace-ID:
          $ref: '#/components/headers/XTraceID'
        Retry-After:
          $ref: '#/components/headers/RetryAfter'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            unavailable:
              value:
                status: error
                error:
                  code: QUEUE_UNAVAILABLE
                  message: Sending is temporarily unavailable
                  type: queue
                  http_status: 503
                  retryable: true
                  doc_url: https://docs.blujay.io/errors/queue-unavailable
                  details: []
                  retry_after: 30
                trace_id: trc_8f2a1c0e4b7d
                timestamp: '2026-10-14T00:00:00.000Z'
  securitySchemes:
    PublicApiKey:
      type: http
      scheme: bearer
      bearerFormat: blu_...
      description: >
        Your workspace API key, sent as `Authorization: Bearer blu_...`.
        Workspace admins can find it in the Blujay app under **API**.


        An API key has full access to your workspace and works on every
        endpoint. There is no test mode: every key sends real messages and
        changes real data, so keep it secret and use it only from your server.


        Each API key can make 600 read requests and 120 write requests per
        minute, plus 60 attachment requests per minute. Over a limit you get
        `429 RATE_LIMIT_EXCEEDED` with `Retry-After`.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.