curl --request DELETE \
--url https://api.flow.engineer/v1/messages/{message_id} \
--header 'Authorization: Bearer <token>'const options = {method: 'DELETE', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.flow.engineer/v1/messages/{message_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.flow.engineer/v1/messages/{message_id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.delete(url, headers=headers)
print(response.text)package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.flow.engineer/v1/messages/{message_id}"
req, _ := http.NewRequest("DELETE", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"id": "msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2",
"conversation": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"direction": "in",
"status": "received",
"content": {
"type": "text",
"text": "<string>",
"format": "plain"
},
"livemode": true,
"created_at": "2023-11-07T05:31:56Z",
"delivered_as": {
"type": "text",
"reason": "<string>"
},
"error": {
"type": "invalid_request",
"message": "<string>",
"hint": "Send a template instead: POST /v1/messages with content.type=template.",
"doc_url": "https://api.flow.engineer/docs/errors/outside_window",
"param": "<string>",
"retry_after": 1,
"conversation": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"sender": "snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T",
"channel_code": "<string>",
"request_id": "<string>"
},
"channel_message_id": "<string>",
"reply_to": "msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2",
"metadata": {},
"updated_at": "2023-11-07T05:31:56Z"
}{
"error": {
"type": "authentication",
"message": "No valid API key was given.",
"hint": "Send the header Authorization: Bearer fk_test_... (or fk_live_...); no key yet? Get a test key with curl -X POST https://api.flow.engineer/v1/sandbox/keys",
"doc_url": "https://api.flow.engineer/docs/errors/authentication"
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"hint": "Send a template instead: POST /v1/messages with content.type=template.",
"doc_url": "https://api.flow.engineer/docs/errors/outside_window",
"param": "<string>",
"retry_after": 1,
"conversation": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"sender": "snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T",
"channel_code": "<string>",
"request_id": "<string>"
}
}{
"error": {
"type": "unsupported_content",
"message": "iMessage cannot show buttons.",
"hint": "Set \"fallback\": \"auto\" to send numbered text instead, or send text.",
"doc_url": "https://api.flow.engineer/docs/errors/unsupported_content",
"param": "content.type"
}
}{
"error": {
"type": "new_contact_limit",
"message": "This sender has started its 15 new conversations for today.",
"hint": "Retry after 3600 seconds; replies into existing conversations still go.",
"doc_url": "https://api.flow.engineer/docs/errors/new_contact_limit",
"retry_after": 3600,
"sender": "snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"hint": "Send a template instead: POST /v1/messages with content.type=template.",
"doc_url": "https://api.flow.engineer/docs/errors/outside_window",
"param": "<string>",
"retry_after": 1,
"conversation": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"sender": "snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T",
"channel_code": "<string>",
"request_id": "<string>"
}
}Unsend a message
Removes one of your outbound messages from the contact’s chat, where the
channel allows it (Telegram: yes, within 48 hours; iMessage: yes, within
2 minutes of sending; WhatsApp: no, 422 unsupported_content). The unsend passes the send gate and goes out in order with the
conversation’s other messages; the message stays in your history and takes
status unsent once the channel removed it. Repeating the call is safe.
curl --request DELETE \
--url https://api.flow.engineer/v1/messages/{message_id} \
--header 'Authorization: Bearer <token>'const options = {method: 'DELETE', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.flow.engineer/v1/messages/{message_id}', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.flow.engineer/v1/messages/{message_id}"
headers = {"Authorization": "Bearer <token>"}
response = requests.delete(url, headers=headers)
print(response.text)package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.flow.engineer/v1/messages/{message_id}"
req, _ := http.NewRequest("DELETE", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}{
"id": "msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2",
"conversation": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"direction": "in",
"status": "received",
"content": {
"type": "text",
"text": "<string>",
"format": "plain"
},
"livemode": true,
"created_at": "2023-11-07T05:31:56Z",
"delivered_as": {
"type": "text",
"reason": "<string>"
},
"error": {
"type": "invalid_request",
"message": "<string>",
"hint": "Send a template instead: POST /v1/messages with content.type=template.",
"doc_url": "https://api.flow.engineer/docs/errors/outside_window",
"param": "<string>",
"retry_after": 1,
"conversation": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"sender": "snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T",
"channel_code": "<string>",
"request_id": "<string>"
},
"channel_message_id": "<string>",
"reply_to": "msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2",
"metadata": {},
"updated_at": "2023-11-07T05:31:56Z"
}{
"error": {
"type": "authentication",
"message": "No valid API key was given.",
"hint": "Send the header Authorization: Bearer fk_test_... (or fk_live_...); no key yet? Get a test key with curl -X POST https://api.flow.engineer/v1/sandbox/keys",
"doc_url": "https://api.flow.engineer/docs/errors/authentication"
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"hint": "Send a template instead: POST /v1/messages with content.type=template.",
"doc_url": "https://api.flow.engineer/docs/errors/outside_window",
"param": "<string>",
"retry_after": 1,
"conversation": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"sender": "snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T",
"channel_code": "<string>",
"request_id": "<string>"
}
}{
"error": {
"type": "unsupported_content",
"message": "iMessage cannot show buttons.",
"hint": "Set \"fallback\": \"auto\" to send numbered text instead, or send text.",
"doc_url": "https://api.flow.engineer/docs/errors/unsupported_content",
"param": "content.type"
}
}{
"error": {
"type": "new_contact_limit",
"message": "This sender has started its 15 new conversations for today.",
"hint": "Retry after 3600 seconds; replies into existing conversations still go.",
"doc_url": "https://api.flow.engineer/docs/errors/new_contact_limit",
"retry_after": 3600,
"sender": "snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T"
}
}{
"error": {
"type": "invalid_request",
"message": "<string>",
"hint": "Send a template instead: POST /v1/messages with content.type=template.",
"doc_url": "https://api.flow.engineer/docs/errors/outside_window",
"param": "<string>",
"retry_after": 1,
"conversation": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"sender": "snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T",
"channel_code": "<string>",
"request_id": "<string>"
}
}Authorizations
An API key of one app, sent as Authorization: Bearer <key>. Keys start with
fk_test_ (test mode: sandbox senders and test data only) or fk_live_
(live mode). Keep live keys on your server; never ship them in an app or page.
Headers
The API version to use, as a date. Without it, the version pinned to your app when it was created is used.
"2026-11-01"
Path Parameters
The message's ID.
A message ID, msg_ and a ULID.
^msg_[0-9A-HJKMNP-TV-Z]{26}$"msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"
Response
The message was unsent.
One message in or out of a conversation, with its typed content and delivery status.
A message ID, msg_ and a ULID.
^msg_[0-9A-HJKMNP-TV-Z]{26}$"msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"
A conversation ID, conv_ and a ULID.
^conv_[0-9A-HJKMNP-TV-Z]{26}$"conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"
in from the contact, out from your app.
in, out received: inbound from the contact.queued: accepted by the gate, waiting for its turn in the conversation.sent: the channel accepted it.delivered: it reached the contact's device, where the channel reports this.read: the contact read it, where the channel reports this.failed: it could not be sent; seeerror.unsent: you unsent it.
received, queued, sent, delivered, read, failed, unsent Text, both ways. With format markdown, Flow renders it in each channel's own formatting; a channel without formatting needs fallback.
- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
- Option 6
- Option 7
- Option 8
- Option 9
- Option 10
- Option 11
- Option 12
- Option 13
- Option 14
- Option 15
Show child attributes
Show child attributes
Whether the message belongs to live mode.
When Flow received (inbound) or accepted (outbound) the message.
Present when what the contact sees differs from what you sent: the fallback
was used, or the action was skipped because the channel has no equivalent.
Absent when the content was shown as sent.
Show child attributes
Show child attributes
The details of an error.
Show child attributes
Show child attributes
The channel's own ID for the message, once the channel accepted it.
The Flow ID of the message this one replies to. For a sent message, the
message the send named in reply_to. For a received message, the message it
replies to inline (a Telegram reply; on iMessage, the thread's root message).
Absent when the message is not a reply or the quoted message is not known
to Flow.
^msg_[0-9A-HJKMNP-TV-Z]{26}$"msg_01JB8ZD4M6P8R0T2V4X6Z8B0C2"
Up to 20 string pairs of your own, kept with the object and returned unchanged. Keys up to 40 characters, values up to 500.
Show child attributes
Show child attributes
When the status last changed.