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

# Add a reaction

> Puts the line's reaction on a message of the line, or replaces the reaction already there.

* **Credential:** an API key. A trial key returns `401 unauthorized`. See [Reactions](/guides/conversations#reactions).
* **Line-scoped keys:** a [line-scoped key](/authentication#line-scoped-keys) gets `404 not_found` for a message on a line outside its list.
* **Test keys:** a reaction sent with an `isnap_test_` account key reaches the real contact: see [Test keys](/authentication#test-keys).
* **`Idempotency-Key`:** honored. The key shares the space of the line with `POST /v1/messages`. See [Retrying a reaction](/guides/conversations#retrying-a-reaction).
* **Events:** sends `reaction.added` when the line's device confirms the reaction. Sends nothing when the device fails to place it.
* **Response:** `202` means requested, not placed. `reactions` shows the reaction as soon as the API accepts it, and keeps showing it when the line's device fails to place it. Wait for `reaction.added`.
* **The line's device:** the reaction needs a device that supports reactions. See [What reaches the contact](/guides/conversations#what-reaches-the-contact).
* **Sending limits:** a reaction counts against none, and is never held.

## Errors

| Status | `error.code` | When |
| - | - | - |
| 400 | `validation_failed` | `type` is not `love`, `like`, `dislike`, `laugh`, `emphasize` or `question`, or the id is malformed. |
| 400 | `line_not_active` | The message's line is neither `active` nor `suspended`. |
| 403 | `line_suspended` | The message's line is suspended. |
| 404 | `not_found` | The message is unknown, belongs to another account, or is on a line outside the list of a line-scoped key. |
| 409 | `idempotency_conflict` | The `Idempotency-Key` was already used on this line for another call, or for this call when a later call has changed the type of the reaction. |
| 422 | `sms_reactions_not_supported` | The message was sent or received as SMS or RCS. |
| 422 | `multi_attachment_reaction` | The message has more than one attachment. |
| 422 | `parent_not_yet_acked` | The line's device has not confirmed the message, or the message was typed on the line's device. |
| 422 | `reaction_recipient_unsupported` | The contact's handle is longer than 20 characters, as most email addresses are. |

The errors that every operation shares are in [Errors](/errors-and-rate-limits#errors).


## OpenAPI

````yaml POST /v1/messages/{id}/reactions
openapi: 3.0.0
info:
  title: iSnap API
  version: 2.45.0
  description: >-
    The iSnap API sends and receives iMessage, RCS and SMS on real phone lines,
    manages those lines, and reports events through webhooks. Every message is
    sent to a real recipient, with an `isnap_live_` key and with an
    `isnap_test_` key alike. Retry a `429` after `Retry-After` seconds. Retry a
    `500` or a `503` up to 2 times, with exponential backoff. Do not retry any
    other `4xx`: fix the request. Retry a write only on an operation that honors
    `Idempotency-Key`, with the key of the first attempt. Responses carry
    personal data: handles, names and message text.
servers:
  - url: https://api.isnap.ai
security:
  - bearerAuth: []
paths:
  /v1/messages/{id}/reactions:
    post:
      summary: Add a reaction
      description: >-
        Puts the line's reaction on a message of the line, received or sent.
        `type` is `love`, `like`, `dislike`, `laugh`, `emphasize` or `question`.
        A line has at most one reaction per message: a request with another
        `type` replaces it, and a request with the `type` already in place
        changes nothing and returns the existing reaction, even when its
        `status` is `failed`. To try a failed reaction again, remove it, then
        add it again.


        Returns `202`: the reaction is handed to the line's device like a
        message. `reaction.added` is sent when the device confirms it. If the
        device reports a failure, no event is sent and the reaction's `status`
        becomes `failed`. If the line has no paired device, the reaction stays
        `queued` and no event follows. The `reactions` list of the message shows
        the reaction as soon as this request is accepted, before the device
        confirms it, and keeps showing it when the device reports a failure.


        The message must be an iMessage message that the line's device has
        confirmed, with at most one attachment, and the handle of its contact
        must be 20 characters or fewer, which every phone number is. For a
        message you sent, wait for `message.sent`. A message typed on the line's
        device itself is always refused with `422 parent_not_yet_acked`. The
        line must be `active`.


        Reactions do not count against the sending limits and are not held by
        quiet hours or by the line's spacing between messages. Accepts an API
        key. Send an `Idempotency-Key` header to make a retry safe: the same key
        with the same message and `type` returns the reaction as it is now. The
        same key with another message or another `type`, and a key that the line
        already used for a removal or for `POST /v1/messages`, return `409
        idempotency_conflict`. The key shares the line's keys with `POST
        /v1/messages`, and the reverse does not conflict: a `POST /v1/messages`
        whose key the line already used for a reaction returns `202` with that
        reaction entry and sends no message. Use a new key for every action.
      parameters:
        - schema:
            type: string
            pattern: ^msg_[0-9A-Za-z]{25}$
            description: 'Message id: `msg_` followed by 25 letters or digits.'
          required: true
          description: 'Message id: `msg_` followed by 25 letters or digits.'
          name: id
          in: path
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SendReactionRequest'
      responses:
        '202':
          description: The reaction, accepted and handed to the line's device.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReactionEnvelope'
        '400':
          description: >-
            Invalid request. `error.code` is `validation_failed` (`type` is not
            one of the six values, or the id is malformed) or `line_not_active`
            (the status of the message's line is neither `active` nor
            `suspended`).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: No valid credential. `error.code` is `unauthorized`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '403':
          description: >-
            Refused. `error.code` is `forbidden` (the request carries neither an
            API key in its `Authorization` header nor the header
            `X-Requested-With: XMLHttpRequest`) or `line_suspended` (the
            message's line is suspended).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: >-
            Not found. `error.code` is `not_found`: the message does not exist,
            belongs to another account, or is on a line outside the key's
            `line_ids`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '422':
          description: >-
            The request breaks a rule of this operation. `error.code` is
            `sms_reactions_not_supported` (the `service` of the message is `SMS`
            or `RCS`), `multi_attachment_reaction` (the message has more than
            one attachment), `parent_not_yet_acked` (the line's device has not
            confirmed the message) or `reaction_recipient_unsupported` (the
            contact's handle is longer than 20 characters, as an email address
            can be).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          description: >-
            Too many requests. `error.code` is `rate_limit_exceeded` when more
            than 100 requests came from the same network address in 60 seconds,
            whatever their API key or account: wait `error.retry_after` seconds.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '500':
          description: >-
            Unexpected failure. `error.code` is `internal_error`. An operation
            that takes a JSON body also returns it when the body, sent as
            `application/json`, is not valid JSON. Quote `trace_id` when you
            report it to iSnap.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
      security:
        - bearerAuth: []
components:
  schemas:
    SendReactionRequest:
      type: object
      properties:
        type:
          type: string
          enum:
            - love
            - like
            - dislike
            - laugh
            - emphasize
            - question
          description: >-
            The reaction to put on the message. It replaces the line's reaction
            on that message when there is one.
      required:
        - type
      description: The reaction to add.
    ReactionEnvelope:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - true
          description: Always `true` on a success response.
        data:
          $ref: '#/components/schemas/Reaction'
        trace_id:
          type: string
          description: >-
            Trace id of the request that produced this response: 32 lowercase
            hexadecimal characters. Quote it when you report a problem to iSnap.
        request_id:
          type: string
          description: >-
            Id of the request that produced this response: `req_` followed by 25
            letters or digits.
      required:
        - success
        - data
        - trace_id
        - request_id
    ErrorEnvelope:
      type: object
      properties:
        success:
          type: boolean
          enum:
            - false
          description: Always `false` on an error response.
        data:
          nullable: true
          description: Always `null` on an error response.
        error:
          type: object
          properties:
            status:
              type: integer
              description: The HTTP status of the response.
            code:
              type: string
              description: >-
                Stable machine-readable code that names the refusal. Branch on
                this value.
            message:
              type: string
              description: >-
                Text for a person. It can change between versions: do not branch
                on it.
            retry_after:
              type: integer
              nullable: true
              description: >-
                Seconds to wait before the next attempt. Set on the `429`
                responses that know a delay, where the `Retry-After` header
                carries the same value. When it is above 60 seconds, do not wait
                in a request: schedule the call, or report the time. `null` on
                every other error.
            retry_at:
              type: string
              nullable: true
              description: >-
                ISO 8601 timestamp at which the refusal ends. Set only on `429
                rebind_cap_reached`. `null` on every other error.
            issues:
              type: array
              nullable: true
              items:
                type: object
                properties:
                  path:
                    type: array
                    items:
                      anyOf:
                        - type: string
                        - type: number
                    description: >-
                      Where the problem is: the field names and array indexes
                      that lead to it.
                  message:
                    type: string
                    description: Text for a person.
                  code:
                    type: string
                    description: >-
                      Kind of problem. On `validation_failed`, for example
                      `invalid_type`, `invalid_value`, `invalid_format`,
                      `invalid_union`, `invalid_key`, `invalid_element`,
                      `too_small`, `too_big`, `not_multiple_of`,
                      `unrecognized_keys` or `custom`. The list is open: read
                      `path` and `message`.
                  min:
                    type: integer
                    description: >-
                      Smallest accepted value. Present only on
                      `limit_out_of_bounds`, `mac_quantity_not_a_pack`,
                      `byop_reduction_strands_mac_sessions` and
                      `byop_reduction_strands_byod_lines`.
                  max:
                    type: integer
                    description: >-
                      Largest accepted value. Present only on
                      `limit_out_of_bounds` and
                      `mac_sessions_exceed_byop_lines`, and on the `too_big`
                      issue of an `Idempotency-Key` header longer than 255
                      characters.
                required:
                  - path
                  - message
                  - code
              description: >-
                Problems found in the request, one entry each. Set on most `400
                validation_failed` responses and on the refusals that name a
                field: a refused `handle` of `POST /v1/lookup` has `issues:
                null`. `null` on every other error.
          required:
            - status
            - code
            - message
            - retry_after
            - retry_at
            - issues
          description: >-
            What went wrong. The six fields are always present; `retry_after`,
            `retry_at` and `issues` are `null` when they do not apply.
        trace_id:
          type: string
          description: >-
            Trace id of the request that produced this response: 32 lowercase
            hexadecimal characters. Quote it when you report a problem to iSnap.
        request_id:
          type: string
          description: >-
            Id of the request that produced this response: `req_` followed by 25
            letters or digits.
      required:
        - success
        - data
        - error
        - trace_id
        - request_id
      description: The body of an error response.
    Reaction:
      type: object
      properties:
        id:
          type: string
          description: >-
            Id of the reaction: `msg_` followed by 25 letters or digits. `GET
            /v1/messages` lists the reaction as an entry whose `kind` is
            `reaction`.
        parent_message_id:
          type: string
          description: Id of the message the reaction is on.
        type:
          type: string
          enum:
            - love
            - like
            - dislike
            - laugh
            - emphasize
            - question
          description: The line's reaction on the message.
        direction:
          type: string
          enum:
            - outbound
            - inbound
          description: 'Always `outbound` here: the line''s own reaction.'
        status:
          type: string
          description: >-
            `queued`: accepted, and the line's device has not confirmed it.
            `sent`: the device confirmed the reaction. `failed`: the device
            reported a failure, and the reaction stays in `reactions` of the
            message until you remove it.
        created_at:
          type: string
          description: When iSnap recorded the reaction.
      required:
        - id
        - parent_message_id
        - type
        - direction
        - status
        - created_at
      description: >-
        The line's reaction on a message, as accepted by `POST
        /v1/messages/{id}/reactions`.
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: >-
        An API key created in the iSnap dashboard, or a trial key, sent as
        `Authorization: Bearer <key>`. An API key starts with `isnap_live_` or
        `isnap_test_`; both act on the same account, lines and billing. A trial
        key acts on its trial only.

````

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