Messages
Instagram direct messages and Facebook Page messages, normalized into one shape. Adeli refreshes history from the provider before returning it, so a read is live rather than a cache.
Meta providers only
TikTok messaging is not supported, and YouTube has no messaging API at all — its direct messages were retired in 2019. WhatsApp sends approved templates rather than free text through the API — see WhatsApp below.
Both providers reply inside a 24-hour window
Meta only permits a reply within 24 hours of the person's last message, on
Instagram and on Facebook alike. When Meta refuses a send because the window
has closed, Adeli returns 409 window_closed; you cannot open one by sending
first.
GET/api/v1/messagesList messages
Returns a flat array across the profile's Instagram, Facebook, and WhatsApp
conversations, oldest first by occurredAt. Uses the
aggregate response contract.
Query parameters
platform"instagram" | "facebook" | "whatsapp"- Return one provider's messages only.
facebookcovers every connected Facebook Page's Messenger conversations. Anything else, includingtiktokandyoutube, returns422 unsupported_platform. accountIduuid- Narrow to one connected account.
profileIduuid- Defaults to your key's profile.
{
"status": "complete",
"messages": [
{
"id": "instagram_message_aWdfbWlk",
"providerId": "aWdfbWlk",
"platform": "instagram",
"accountId": "00000000-0000-0000-0000-000000000000",
"profileId": "00000000-0000-4000-8000-000000000001",
"direction": "inbound",
"type": "text",
"recipient": null,
"sender": "17841400000000001",
"body": "Do you ship to Canada?",
"status": "received",
"occurredAt": "2026-04-01T12:00:00.000Z",
"createdAt": "2026-04-01T12:00:01.000Z"
}
],
"errors": []
}Message fields
idstring- Adeli's identifier for this message.
providerIdstring | null- The provider's message id.
direction"inbound" | "outbound"- Relative to the connected account.
type"text" | "template" | "unsupported"unsupportedcovers media and other payloads Adeli does not normalize.senderstring | null- Set on inbound messages — the provider-scoped participant id (an IGSID on Instagram, a PSID on Facebook).
recipientstring | null- Set on outbound messages.
statusstring- Provider delivery state, such as
receivedorsent. occurredAtISO 8601- When the provider recorded the message. Sort on this.
If a provider sync fails, Adeli still returns the messages it has already
persisted and reports the failure in errors, which makes the response 207.
Errors — 401 unauthorized, 400 invalid_request,
422 unsupported_platform, 404 profile_not_found, 404 account_not_found,
502 provider_error.
POST/api/v1/messagesSend a message
Body
platform"instagram" | "facebook"RequiredaccountIduuidRequired- A connected Instagram account or Facebook Page in your key's profile.
profileIduuid- Defaults to your key's profile.
recipientstringRequired- The provider-scoped participant id, 1–200 characters. Take it from the
senderfield of an inbound message, not from a username. textstringRequired- 1–1,000 characters.
curl --fail-with-body -X POST "$ADELI_URL/api/v1/messages" \
-H "Authorization: Bearer $ADELI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"platform": "instagram",
"accountId": "'"$ACCOUNT_ID"'",
"recipient": "'"$RECIPIENT_ID"'",
"text": "Yes, we ship to Canada."
}'A Facebook reply has the same shape. accountId is the connected Page, and
recipient is the person's PSID, from the sender field of their inbound
message:
curl --fail-with-body -X POST "$ADELI_URL/api/v1/messages" \
-H "Authorization: Bearer $ADELI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"platform": "facebook",
"accountId": "'"$PAGE_ACCOUNT_ID"'",
"recipient": "'"$PSID"'",
"text": "Yes, we deliver on Saturdays."
}'Success returns 201 and a bare message object with direction: "outbound",
type: "text", and status: "sent".
Meta's messaging rules still apply
A business can message a person only inside the window Meta's policy opens,
which begins when that person messages the business. When Meta refuses a send
because that window has closed, the response is 409 window_closed. Wait for
the person to message again before retrying.
Errors — 401 unauthorized, 400 invalid_request, 404 profile_not_found,
404 account_not_found, 409 window_closed, 409 page_not_selected (Facebook),
403 missing_permission (Facebook), 422 connection_expired,
422 provider_not_configured, 422 provider_not_supported,
502 provider_error.
Each profile can hold one WhatsApp number that the business connected itself,
through Embedded Signup on the dashboard's Accounts page. It appears in
GET /api/v1/accounts with platform: "whatsapp", the phone number ID as
providerId, and the display number as displayIdentifier, and its history is
included in GET /api/v1/messages. WhatsApp history is what Adeli has received
by webhook and sent, so a read makes no provider call.
Through the API, WhatsApp sends one of the business's approved templates — the only message WhatsApp allows when starting a conversation or more than 24 hours after the customer last wrote.
WhatsApp body
platform"whatsapp"RequiredaccountIduuidRequired- The WhatsApp account in your key's profile.
profileIduuid- Defaults to your key's profile.
recipientstringRequired- An E.164 phone number, such as
+15551234567. template.namestringRequired- An approved template on the business's WhatsApp Business Account: lowercase letters, digits, and underscores.
template.languagestringRequired- The template's language, such as
en_US. template.bodyParametersstring[]- Values for the body's
{{1}},{{2}}, … placeholders, in order. Up to 20; omit for a template without placeholders.
{
"platform": "whatsapp",
"accountId": "00000000-0000-0000-0000-000000000000",
"recipient": "+15551234567",
"template": { "name": "order_update", "language": "en_US", "bodyParameters": ["Jane", "A-42"] }
}Success returns 201 with type: "template". A number whose setup has not
finished returns 422 setup_incomplete; one whose access the business revoked
returns 422 reconnect_required. An unknown or unapproved template fails at
Meta and surfaces as 502 provider_error.
WhatsApp numbers cannot be connected through POST /api/v1/profiles/{profileId}/connect
yet — that returns 422 provider_not_supported.