curl --request GET \
--url https://api.flow.engineer/v1/events \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.flow.engineer/v1/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.flow.engineer/v1/events"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.flow.engineer/v1/events"
req, _ := http.NewRequest("GET", 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))
}{
"data": [
{
"id": "evt_01JB8ZE5N7Q9S1V3X5Z7B9D1E3",
"created_at": "2023-11-07T05:31:56Z",
"app": "app_01JB8Z0A1C3E5G7J9K1M3P5R7T",
"livemode": true,
"timing": {
"received_at": "2023-11-07T05:31:56Z",
"stored_at": "2023-11-07T05:31:56Z",
"delivered_at": "2023-11-07T05:31:56Z"
},
"type": "message.received",
"data": {
"message": {
"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"
}
},
"conversation": {
"id": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"channel": "telegram",
"sender": "snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T",
"contact": "ct_01JB8ZB2J4K6N8Q0S2V4W6Y8A0",
"window_open_until": "2023-11-07T05:31:56Z"
}
}
],
"has_more": true
}{
"error": {
"type": "invalid_request",
"message": "limit must be between 1 and 100.",
"hint": "Pass limit between 1 and 100 (default 20), and page with after or before.",
"doc_url": "https://api.flow.engineer/docs/errors/invalid_request",
"param": "limit"
}
}{
"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": "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>"
}
}List events
Reads your app’s event log in this mode, oldest first. Pass the ID of the
last event you processed as after to catch up after downtime, or to replay
from any point. The log is the same one webhooks and the stream deliver from,
so it is also how you recover events whose webhook deliveries failed.
The log only returns events once every event before them is committed, so
reading forward with after never skips an event.
curl --request GET \
--url https://api.flow.engineer/v1/events \
--header 'Authorization: Bearer <token>'const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.flow.engineer/v1/events', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://api.flow.engineer/v1/events"
headers = {"Authorization": "Bearer <token>"}
response = requests.get(url, headers=headers)
print(response.text)package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.flow.engineer/v1/events"
req, _ := http.NewRequest("GET", 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))
}{
"data": [
{
"id": "evt_01JB8ZE5N7Q9S1V3X5Z7B9D1E3",
"created_at": "2023-11-07T05:31:56Z",
"app": "app_01JB8Z0A1C3E5G7J9K1M3P5R7T",
"livemode": true,
"timing": {
"received_at": "2023-11-07T05:31:56Z",
"stored_at": "2023-11-07T05:31:56Z",
"delivered_at": "2023-11-07T05:31:56Z"
},
"type": "message.received",
"data": {
"message": {
"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"
}
},
"conversation": {
"id": "conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1",
"channel": "telegram",
"sender": "snd_01JB8Z4Q3V6W0R2N7C5H1M9K4T",
"contact": "ct_01JB8ZB2J4K6N8Q0S2V4W6Y8A0",
"window_open_until": "2023-11-07T05:31:56Z"
}
}
],
"has_more": true
}{
"error": {
"type": "invalid_request",
"message": "limit must be between 1 and 100.",
"hint": "Pass limit between 1 and 100 (default 20), and page with after or before.",
"doc_url": "https://api.flow.engineer/docs/errors/invalid_request",
"param": "limit"
}
}{
"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": "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"
Query Parameters
An item ID. Returns the items that come after it in the list's order.
64An item ID. Returns the items that come before it in the list's order.
64How many items to return, 1 to 100.
1 <= x <= 100Only events of these types. Repeat the parameter for several (type=message.received&type=reaction.added).
20message.received: the contact sent something with content (a button tap arrives asbutton_replycontent).message.sent,message.delivered,message.read,message.failed: the status of your outbound messages.message.failedcarries the error indata.message.error.reaction.added,reaction.removed: the contact reacted to a message.typing.started,typing.stopped: the contact is typing, where the channel reports it.conversation.started: the first inbound message from a new contact, or a sandbox join (Flow itself answers the join; the join message is not amessage.received).conversation.window_closing: WhatsApp only, opt-in. The 24-hour window closes in 1 hour.sender.status_changed: a sender was throttled, flagged, banned or restored, or its WhatsApp quality rating changed.template.status_changed: Meta approved, rejected or paused a template.
message.received, message.sent, message.delivered, message.read, message.failed, reaction.added, reaction.removed, typing.started, typing.stopped, conversation.started, conversation.window_closing, sender.status_changed, template.status_changed Only events in this conversation. An ID with no conversation of this app and mode answers 404 not_found.
A conversation ID, conv_ and a ULID.
^conv_[0-9A-HJKMNP-TV-Z]{26}$"conv_01JB8ZC3K5M7P9R1T3V5X7Z9B1"
Response
A page of events, oldest first.
A page of events, oldest first.
One entry in your app's log. type says what happened and selects the shape
of data. conversation is set for every event that happened in a
conversation (all but sender.status_changed and template.status_changed).
- 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
Show child attributes
Show child attributes
Whether more committed events follow this page.