Skip to main content
iMessage lets the sender change a message’s text for a short time after sending it, or take it back completely. You edit with PATCH /v1/messages/{message_id} and unsend with POST /v1/messages/{message_id}/unsend, and the change shows on the recipient’s device. Both endpoints honor Idempotency-Key.

Know which messages you can change

You can edit or unsend a message when all of these are true:
  • Your workspace sent it. Messages your contacts sent can’t be changed.
  • It went out as iMessage. SMS and RCS messages can’t be changed.
  • It has been sent. A failed message, or one that is still sending, can’t be changed.
  • It is text only. Messages with media or contact cards can’t be changed.
  • It hasn’t been unsent already.
Apple’s time limits apply:

Edit a message

Send the new text. Only text-only messages can be edited, so if you send part_index, it must be 0. A text that contains a link counts as having an attachment, because iMessage adds a link preview to it, so it can’t be edited or unsent yet (409 MESSAGE_NOT_EDITABLE).
curl
Blujay returns 200 once the change is confirmed, with the updated message:
Response (200)
parts always holds the current text. edited_at is when the last edit happened and edit_count is how many edits the message has had. Blujay returns 200 once the change is confirmed on your phone number. If it can’t confirm the change in time, you get 503 CHANGE_UNCONFIRMED; retry with the same Idempotency-Key to check. One rule decides what happens when text matches the message’s current text:
  • If the message has been edited before (edit_count is 1 or more), Blujay treats the request as a repeat of that edit and returns 200 with the current message. Nothing is sent again.
  • If the message has never been edited, Blujay returns 400 EDIT_UNCHANGED.
Sending the original text to a message that was later edited is a normal edit back to that text.

Unsend a message

Unsend takes no body.
curl
The 200 response is the message with unsent_at and unsent_scope set and its text removed from parts. Repeating an unsend returns the current message.
Response (200)
Unsent text is never returned by the API, not in parts, not in edit history and not in later webhooks. Recipients on older Apple software may still see the message.

Read edit history

Add include=edit_history to GET /v1/messages/{message_id}, GET /v1/messages or GET /v1/messages/{message_id}/thread to get the earlier versions of an edited message, oldest first.
curl
An unsent message always has an empty edit_history, because its earlier versions are unsent text too.

Handle errors

A request Blujay rejected with a 4xx isn’t remembered under its Idempotency-Key, so you can fix the request and send it again with the same key.

Track edits and unsends with webhooks

Subscribe to message.edited and message.unsent to hear about changes to messages your workspace sent, whether they were made through the API or elsewhere in Blujay, and about changes your contacts make to their own iMessages. Only messages up to a week old are tracked for contacts’ edits and unsends. A contact’s message carries contact_id in a direct chat or sender in a group, as on message.received.
message.edited
  • message.edited carries the current text, edited_at and edit_count, never earlier versions. Read those with include=edit_history.
  • message.unsent carries unsent_at and unsent_scope, never text. With unsent_scope: "all" the whole message was taken back. "text" only ever applies to a contact’s message: they removed the caption and the attachments remain. Messages you send through the API are text only, so your own unsends are always "all". A later full unsend sends a second event with "all".
  • Events recorded before an unsend keep their original bodies. If you stored the text from an earlier message.received or message.sent, delete it when message.unsent arrives.
  • Deliveries can be repeated or arrive out of order. Deduplicate by event_id, keep the newest edited_at for each message_id, and treat an unsend as final.
Common mistakes
  • Edit and unsend are synchronous. A 200 means the change is confirmed; a 503 CHANGE_UNCONFIRMED means retry with the same key, not that it failed.
  • Editing a message that hasn’t been sent yet returns 409 MESSAGE_NOT_SENT_YET. Wait for sent.
  • Resending the current text of a never-edited message returns 400 EDIT_UNCHANGED; on an edited message it returns 200 with no change.
  • message.edited never carries earlier versions. Read them with include=edit_history.

Limitations

  • iMessage only. SMS and RCS messages return 409 MESSAGE_NOT_EDITABLE.
  • Your workspace’s messages only; a contact’s message can’t be edited or unsent (409 MESSAGE_NOT_EDITABLE).
  • Text-only messages. A message with media or a contact card returns 409 MESSAGE_NOT_EDITABLE.
  • Edit within 15 minutes, up to 5 times (409 EDIT_WINDOW_EXPIRED, 409 EDIT_LIMIT_REACHED). Unsend within 2 minutes (409 UNSEND_WINDOW_EXPIRED).
  • Only part_index 0 can be edited.
  • Contacts’ edits and unsends are tracked for messages up to a week old.
  • Unsent text is never returned, and recipients on older Apple software may still see it.

Next steps

Webhook events

See the full list of message events.

Idempotency

Retry edits and unsends safely after a timeout.

Threaded replies

Reply to a specific message in its iMessage thread.

Track a message

Understand message statuses before you edit or unsend.