Meta & Instagram API Integration
Reference for building an Instagram app on the SMSBAT ChatHub Platform: authentication, Instagram Direct conversations, comments on posts and Reels, Story replies, webhooks and polling.
Sources
This page merges the internal Meta Comments API specification with the live OpenAPI
definitions at https://chatapi.smsbat.com/swagger/v1/swagger.json and
https://restapi.smsbat.com/swagger/v1/swagger.json. Where the two disagree, the
difference is called out inline and listed under Open questions.
1. Base URLs
| Purpose | URL |
|---|---|
| Chat API + Meta API | https://chatapi.smsbat.com |
| Swagger UI / OpenAPI | https://chatapi.smsbat.com/index.html · …/swagger/v1/swagger.json |
| REST API (organizations, callback URLs) | https://restapi.smsbat.com |
| REST API Swagger | https://restapi.smsbat.com/swagger/v1/swagger.json |
| Operator web panel | https://chat.smsbat.com |
2. Authentication
The auth scheme depends on the endpoint group. Mixing them up is the most common cause of 401.
| Group | Header |
|---|---|
chatapi.smsbat.com/api/meta/* (posts, comments) | X-Authorization-Key: <organization token> |
chatapi.smsbat.com/api/chat/*, /api/company/*, /api/operator/* | Authorization: Bearer <JWT> |
restapi.smsbat.com/* | X-Authorization-Key · Authorization: Bearer · Basic Auth |
The organization token for X-Authorization-Key is issued in the panel under Profile.
Company and operator JWTs come from /api/company/get-token and /api/operator/get-token.
Discrepancy
The chatapi OpenAPI document declares a single security scheme — Bearer — and applies it
globally. X-Authorization-Key is not declared there at all, although the internal Meta
Comments API specification names it for /api/meta/*. It is most likely handled by
middleware that is not reflected in Swagger. Confirm empirically before you ship.
2.1 Company token
POST https://chatapi.smsbat.com/api/company/get-token
Content-Type: application/json
{ "login": "company_login", "password": "company_password" }
200 OK returns a bare token string.
2.2 Organizations
GET https://chatapi.smsbat.com/api/company/organization
Authorization: Bearer <company_token>
[ { "id": 24, "name": "My Instagram Store" } ]
2.3 Operators in an organization
GET https://chatapi.smsbat.com/api/operator?organizationId=24
Authorization: Bearer <company_token>
[
{
"id": 21,
"name": "Jane Doe",
"status": 0,
"organization": { "id": 24, "name": "My Instagram Store" }
}
]
Operator statuses: 0 Active, 1 Inactive, 2 Deleted.
2.4 Add / synchronize operators
POST https://chatapi.smsbat.com/api/operator/synchronize
Authorization: Bearer <company_token>
Content-Type: application/json
[ { "organizationId": 24, "name": "John Operator" } ]
200 OK → [ { "id": 21, "status": 0, "name": "John Operator" } ]
2.5 Operator JWT
POST https://chatapi.smsbat.com/api/operator/get-token
Authorization: Bearer <company_token>
Content-Type: application/json
{ "id": 21, "expiresAt": "2026-12-31T23:59:59.000Z" }
200 OK returns the JWT as a string.
2.6 Validate an operator token
POST https://chatapi.smsbat.com/api/operator/validate-token
Authorization: Bearer <company_token>
Content-Type: application/json
"eyJhbGciOi..."
{
"isValid": true,
"operatorId": 21,
"clientId": 0,
"expiresAt": "2026-12-31T23:59:59.000Z",
"error": null
}
When invalid: { "isValid": false, "error": "Invalid token" }.
2.7 Embed the operator chat panel
<script type="module" id="operator-chat-panel-script"
src="https://widget.smsbat.com/operator-chat-panel/widget-script.js"
token="YOUR_OPERATOR_JWT_TOKEN"></script>
3. Deeplinks into the chat panel
An external system (CRM, ERP, website) can open a specific conversation in
https://chat.smsbat.com/. The operator is authorized by a JWT passed as a query parameter.
https://chat.smsbat.com/?chat_raw_id=<chat_id>&token=<jwt>
https://chat.smsbat.com/?phone=<phone>&token=<jwt>
https://chat.smsbat.com/?from=<bm_id>&phone=<phone>&token=<jwt>
https://chat.smsbat.com/?source=7&from=<bm_id>&phone=<phone>&token=<jwt>
| Parameter | Description |
|---|---|
chat_raw_id | Chat ID |
phone | Phone number in international format |
from | Brand / business account identifier (bm_id) |
source | Chat source — 7 for Instagram, see §8.1 |
token | Valid, unexpired operator JWT with access to chats |
An invalid JWT lands the visitor on the operator panel’s login screen.
4. Instagram Direct conversations
4.1 List chats
GET https://chatapi.smsbat.com/api/chat/chats?source=7&page=1&per_page=20
Authorization: Bearer <token>
Note
Pagination here is per_page (snake_case). Under /api/meta/* and the polling
endpoint it is perPage (camelCase). This is not a typo — the API uses both.
Query parameters, all optional:
| Parameter | Type | Description |
|---|---|---|
source | ChatSource | 7 restricts results to Instagram |
entityId | int | Business account ID. Only applied together with source |
instagram_user_id | int | Instagram user ID in ChatHub |
facebook_user_id | int | Facebook user ID in ChatHub |
page / per_page | int | Pagination, defaults 1 / 20 |
status | ChatStatus[] | Chat status, repeatable |
search | string | Free-text search (name, phone, …) |
organizationId | int | Organization ID |
operatorId | int[] | Filter by assigned operators |
date | string[] | Two bounds: ?date=…&date=… |
isChain | bool | Return chats as chains, carrying messages from previous chats |
isUnread, starMark, isOperator, isAIAgent | bool | Additional filters |
phone, email, contactId, clientId, tagIds, rate, sortedBy | — | Other filters |
200 OK returns GetChatsResponse:
{
"total": 1,
"newMessagesCount": 2,
"items": [
{
"id": 1867,
"theme": null,
"messSource": 7,
"chatStatus": 1,
"countUnread": 2,
"metaUserId": "1585775752382460",
"instaAccount": { "id": 12, "name": "my_instagram_shop", "photo": "https://..." },
"instagramUser": { "id": 123, "name": "marianna_cat", "photo": "https://..." },
"operator": { "id": 21, "name": "Jane", "photo": "https://..." },
"client": { "id": 55, "name": "Marianna", "photo": null },
"textLastMess": "Hello! Is this product available?",
"timeLastMess": "2026-08-13T10:15:00Z",
"authorLastMessage": 1,
"messageStatus": 6,
"isMedia": false,
"phone": null,
"organizationId": 1,
"createdAt": "2026-08-13T10:14:00Z",
"isStarred": false,
"isBlocked": false,
"tags": []
}
]
}
The fields that matter for an Instagram app:
| Field | Meaning |
|---|---|
instaAccount | The Instagram business account (the shop). id is the entityId filter value; name is the account name from Meta |
instagramUser | The customer. name is the Instagram handle, id is the instagram_user_id filter value |
metaUserId | The customer’s scoped ID on Meta’s side (string) |
messSource | 7 for Instagram |
phone | Usually null for Instagram — do not use it as a key |
ChatDTO also carries olxUser, promUser, waba, rozetkaUser, facebookAccount,
facebookUser, viberAccount, tgBot, tgUser, viberBot, viberBotUser, widget,
media, isConnectedAI, isPromo, rate, rateComment, closedBy, draft, lang,
starMark, contactId, contactName, block, isInStopList, stopList,
isSmsFallbackEnabled and taggedMessages.
4.2 Chat messages
GET https://chatapi.smsbat.com/api/chat/chats/1867/messages?isChain=false
Authorization: Bearer <token>
200 OK returns an array of ChatMessageDTO:
[
{
"id": 9928,
"chatId": 1867,
"message": "Hi! Do you have size M in stock?",
"messageTranslation": null,
"phone": null,
"author": 1,
"source": 7,
"media": null,
"replyTo": null,
"status": 6,
"date": "2026-08-13T10:14:00Z",
"operator": null,
"client": { "id": 123, "name": "marianna_cat", "photo": "https://..." },
"messageType": 0,
"isInternal": false,
"isBroadcast": false,
"starMark": false,
"referralGuid": null,
"postId": null,
"isConnectedAI": false,
"organizationId": 1,
"countUnreadMessages": 0
}
]
postId is populated when the message relates to an Instagram post or Story — pass it
straight back as id / postId to the Meta API. media is a ChatMediaDTO:
{ name, format, type, uri, raw, length, isUploaded }.
4.3 Send a message (JSON)
POST https://chatapi.smsbat.com/api/chat/1867/message
Authorization: Bearer <token>
Content-Type: application/json
Body — SendChatMessageDTO:
{
"textMessage": "Hello! Yes, size M is available.",
"author": 0,
"isInternal": false,
"replyToMessageId": 9928,
"appGuid": "550e8400-e29b-41d4-a716-446655440000",
"media": {
"name": "item.jpg",
"format": "image/jpeg",
"dataBase64": "/9j/4AAQSkZJRg...",
"type": 1
}
}
| Field | Type | Description |
|---|---|---|
textMessage | string? | Message text. May be empty when media is present |
author | AuthorMessage? | 0 operator, 1 client |
isInternal | bool? | true marks an internal note that is not delivered to the customer |
replyToMessageId | int? | ID of the message being replied to |
appGuid | uuid? | Referral GUID |
media | MediaDTO? | { name, format, dataBase64, thumbnail, duration, type } |
200 OK → { "id": 9930, "messageStatus": 0 }
A referral GUID may also be passed in the path:
POST /api/chat/{chatId}/{referralGuid}/message (likewise …/message/v1, …/message/v2).
4.4 Send a file or video (multipart, v2)
POST https://chatapi.smsbat.com/api/chat/1867/message/v2
Authorization: Bearer <token>
Content-Type: multipart/form-data
Form field names are PascalCase with dot notation
textMessage and media.file are silently ignored. Use the exact names below.
| Form field | Type | Description |
|---|---|---|
TextMessage | string | Message text |
Author | int | 0 operator, 1 client |
IsInternal | bool | Internal note |
ReplyToMessageId | int | Message being replied to |
AppGuid | uuid | Referral GUID |
Media.File | binary | The file itself |
Media.Name | string | File name |
Media.Format | string | MIME type (video/mp4, image/png, application/pdf) |
Media.Type | MediaType | See §8.5 |
Media.DataBase64 | string | Alternative to Media.File |
Media.Thumbnail | string | Base64 video preview frame |
Media.Duration | double | Video duration in seconds |
curl -X POST "https://chatapi.smsbat.com/api/chat/1867/message/v2" \
-H "Authorization: Bearer <token>" \
-F "TextMessage=Here is the price list" \
-F "Author=0" \
-F "Media.Type=2" \
-F "Media.File=@./price.pdf"
200 OK → { "id": 9931, "messageStatus": 0 }
4.5 Change chat status
PUT https://chatapi.smsbat.com/api/chat/status
Authorization: Bearer <token>
Content-Type: application/json
{ "id": 1867, "status": 4 }
200 OK echoes the updated object.
4.6 Update message statuses
PUT https://chatapi.smsbat.com/api/chat/messages/status
Authorization: Bearer <token>
Content-Type: application/json
{ "status": 3, "messageIds": [9928, 9929] }
4.7 Delete a chat
DELETE https://chatapi.smsbat.com/api/chat/chats/1867
Authorization: Bearer <token>
5. Posts, Reels and Stories
Base path: https://chatapi.smsbat.com/api/meta
Auth: X-Authorization-Key: <organization token>
5.1 List posts, Reels and Stories
GET https://chatapi.smsbat.com/api/meta/posts?platform=instagram&mediaType=story&page=1&perPage=20
X-Authorization-Key: <token>
| Parameter | Type | Required | Description |
|---|---|---|---|
page | int | no | Page, default 1 |
perPage | int | no | Items per page, default 20 |
id | int | no | Filter by internal post ID |
platform | string | no | instagram or facebook |
mediaType | string | no | post, reel or story. All types when omitted |
# Every post
curl -H "X-Authorization-Key: <token>" \
"https://chatapi.smsbat.com/api/meta/posts?page=1&perPage=10"
# Instagram only
curl -H "X-Authorization-Key: <token>" \
"https://chatapi.smsbat.com/api/meta/posts?platform=instagram"
# A single post by ID
curl -H "X-Authorization-Key: <token>" \
"https://chatapi.smsbat.com/api/meta/posts?id=42"
# Instagram Stories only
curl -H "X-Authorization-Key: <token>" \
"https://chatapi.smsbat.com/api/meta/posts?platform=instagram&mediaType=story"
200 OK:
{
"total": 42,
"items": [
{
"id": 42,
"metaId": "18113450675314072",
"text": "Check out our new collection!",
"imageUrl": "https://dashboard.smsbat.com/api/meta/post/media/7d124c36-1711-4aa8-b21d-f6dbf8f347ef",
"platform": "instagram",
"mediaType": "story",
"createdAt": "2026-07-22T09:35:30Z",
"story": {
"id": 42,
"metaId": "18113450675314072",
"url": "https://dashboard.smsbat.com/api/meta/post/media/7d124c36-1711-4aa8-b21d-f6dbf8f347ef"
}
}
]
}
Discrepancy — counter field name
The Swagger schema MetaCommentPostListItemDtoPaginationDTO defines total. The
internal specification documents totalCount. Swagger is generated from the code, so
total is the more likely truth. Parse total ?? totalCount until this is settled.
| Field | Description |
|---|---|
id | Internal post ID |
metaId | External post / Reel / Story ID in Meta |
text | Post caption |
imageUrl | Proxy media URL keyed by the non-sequential MetaPost.Guid, or null |
platform | facebook or instagram |
mediaType | post, reel or story |
createdAt | Creation date (platform date, or the database date) |
story | Present only for mediaType: "story" |
story.id | Internal Story ID; equal to post.id |
story.metaId | External Story ID in Meta |
story.url | Stable proxy URL of the stored Story media; null if the media could not be saved |
Post media is served by two routes: GET /api/meta/post/media/{id:int} for backward
compatibility and GET /api/meta/post/media/{guid:guid}. New API responses and callbacks
always generate the GUID form.
5.2 List comments
GET https://chatapi.smsbat.com/api/meta/comments?platform=instagram&postId=42&page=1&perPage=20
X-Authorization-Key: <token>
| Parameter | Type | Required | Description |
|---|---|---|---|
page | int | no | Page, default 1 |
perPage | int | no | Items per page, default 20 |
postId | int | no | Filter by post ID |
parentCommentId | int | no | Child comments (replies) of a given comment |
platform | string | no | facebook or instagram |
200 OK:
{
"total": 100,
"items": [
{
"id": 5,
"metaId": "179000000000005",
"text": "What is the price?",
"createdAt": "2026-04-15T10:30:00Z",
"platform": "instagram",
"replyStatus": null,
"author": {
"type": "meta_user",
"name": "John Doe",
"metaUserId": "1585775752382460"
},
"post": {
"id": 1,
"metaId": "18113450675314072",
"text": null,
"imageUrl": "https://dashboard.smsbat.com/api/meta/post/media/7d124c36-…",
"createdAt": "2026-07-22T09:35:30Z",
"mediaType": "story",
"story": {
"id": 1,
"metaId": "18113450675314072",
"url": "https://dashboard.smsbat.com/api/meta/post/media/7d124c36-…"
}
},
"mediaUrl": "https://chatapi.smsbat.com/api/meta/comment/media/5",
"replyTo": {
"id": 3,
"metaId": "179000000000003",
"text": "Parent comment..."
}
}
]
}
| Field | Description |
|---|---|
id | Internal comment ID |
metaId | External ID in Meta. null for a pending reply of ours until it is sent |
text | Comment text |
createdAt | Creation date |
platform | facebook or instagram |
replyStatus | null for an inbound user comment; "pending" / "sent" / "failure" for our reply |
author.type | "meta_user" external user, "owner" page owner |
author.name | Author name |
author.metaUserId | Scoped user ID in Meta; null for "owner" |
post | The post, Reel or Story the comment belongs to |
post.mediaType | post, reel or story |
post.story | Story reference { id, metaId, url }, Stories only |
mediaUrl | Media attached to the comment, or null |
replyTo | Parent comment { id, metaId, text }; null at top level |
Discrepancy — type of `author.type`
The internal specification documents the strings "meta_user" / "owner". Swagger types
MetaCommentAuthorType as an integer with enum [0, 1]. A JsonStringEnumConverter
would explain the gap, but that has not been confirmed against a real response. Write a
parser that accepts both.
5.3 Reply to a comment
Queues a reply for delivery.
curl -X POST \
-H "X-Authorization-Key: <token>" \
-H "Content-Type: application/json" \
-d '{"text": "Thanks for the feedback!"}' \
"https://chatapi.smsbat.com/api/meta/comments/5/reply"
Request body: { "text": "Reply text" }
202 Accepted returns the comment object — same shape as GET /api/meta/comments — with
replyStatus: "pending" and metaId: null. The delivery outcome arrives later as a
source: 13 callback (§6.4).
Discrepancy — response code
Swagger declares 200 with no body; the internal specification declares 202 Accepted
with the comment as the body. The controller most likely lacks a ProducesResponseType
attribute, leaving Swagger on its default. Accept any 2xx and do not depend on a body.
6. Webhooks
SMSBAT sends POST requests with application/json to your URL and expects HTTP 200 back.
Null fields are omitted entirely
A field whose value is null is not serialized into the callback body at all. For a
message that did not come from Facebook or Instagram there is simply no MetaUserId key.
Treat “absent” and null as the same thing.
6.1 Register a callback URL
curl -X POST 'https://restapi.smsbat.com/organizations/callback_urls' \
-H 'X-Authorization-Key: <token>' \
-H 'Content-Type: application/json' \
-d '{
"url": "https://your-server.com/webhook",
"source": 12,
"headerName": "X-Webhook-Secret",
"headerValue": "your-secret",
"channelType": 7,
"channelEntityId": 12
}'
| Field | Type | Description |
|---|---|---|
url | string | Your endpoint |
source | SendingSourceCallback | Event type, see §8.2 |
headerName / headerValue | string | Arbitrary auth header we attach to the request (optional) |
channelType | ChatSource | Channel. 7 for Instagram. Optional |
channelEntityId | int | A specific business account. Requires channelType |
Without channelType the URL receives events from every channel.
Tip
Full comment coverage needs two registrations: source: 12 for new comments and
source: 13 for reply statuses. For Direct and Story replies add source: 3
(and 11 if you want every chat message).
Remaining operations:
GET https://restapi.smsbat.com/organizations/callback_urls?source=12
GET https://restapi.smsbat.com/organizations/callback_urls/{id}
PUT https://restapi.smsbat.com/organizations/callback_urls/{id}
DELETE https://restapi.smsbat.com/organizations/callback_urls/{id}
GET returns:
[
{
"id": 101,
"url": "https://your-server.com/webhook",
"source": 12,
"headerName": "X-Webhook-Secret",
"headerValue": "your-secret",
"createdAt": "2026-08-13T09:00:00Z",
"channelType": 7,
"channelEntityId": 12
}
]
6.2 New message and Instagram Story reply (source: 3, 11)
A user’s reply to an Instagram Story arrives as an ordinary message in these callbacks,
with an extra top-level Story block:
{
"ChatId": 123,
"MessageId": 456,
"MessageText": "😍",
"Username": "Jane Smith",
"UserId": 789,
"MetaUserId": "1585775752382460",
"ShopId": 12,
"ShopName": "instagram shop name",
"Author": 0,
"type_messenger": 7,
"Story": {
"Id": 42,
"MetaId": "18113450675314072",
"Url": "https://dashboard.smsbat.com/api/meta/post/media/7d124c36-1711-4aa8-b21d-f6dbf8f347ef"
}
}
| Field | Description |
|---|---|
ChatId / MessageId | Chat and message identifiers |
Author | 0 user, 1 operator |
Username | Instagram / Facebook display name or handle |
UserId | Internal numeric user ID in SMSBAT |
MetaUserId | Scoped ID of the conversation partner in Meta. On an outgoing operator message this still identifies the Meta user of the chat, not the operator |
ShopId | Internal ID of the Instagram / Facebook business account |
ShopName | Business account name as received from Meta at connection time |
MessageText | Message text |
MessageMedia | Media URL when the message is media |
type_messenger | Source, 7 for Instagram |
operator_name | Operator name when Author = 1 |
Story | Present only on an inbound Story reply |
Story.Id | Internal Story (MetaPost) ID — usable directly as id / postId in the Meta API |
Story.MetaId | External Story ID in Meta |
Story.Url | Stable proxy URL of the stored Story media. Absent when the media could not be saved — the Story block and the message are still delivered |
`Author` is inverted relative to the Chat API
In ChatMessageDTO.author, 0 means operator and 1 means client. In this callback it is
the other way round: 0 is the user, 1 is the operator. Do not share the mapping.
6.3 New comment (source: 12)
Fires when a Meta user comments on a Facebook post or an Instagram post / Reel.
Note
Instagram Story replies are not delivered through source: 12. They arrive as ordinary
inbound messages on source: 3 and/or 11 with a Story block — see §6.2.
{
"type": "new_comment",
"platform": "instagram",
"comment": {
"id": 10,
"metaId": "179000000000010",
"parentCommentId": 5,
"parentMetaId": "179000000000005",
"parentCommentText": "The user's previous comment",
"text": "Great product!",
"createdAt": "2026-04-16T12:00:00Z",
"author": {
"type": "meta_user",
"name": "Jane Smith",
"metaUserId": "1585775752382460"
}
},
"post": {
"id": 1,
"metaId": "123456789012345",
"text": "Post description...",
"imageUrl": "https://dashboard.smsbat.com/api/meta/post/media/7d124c36-…",
"createdAt": "2026-04-10T12:00:00Z",
"mediaType": "post"
}
}
6.4 Comment reply status (source: 13)
Fires after we attempt to deliver a reply, whether it succeeds or fails.
{
"type": "comment_status",
"platform": "instagram",
"comment": {
"id": 15,
"metaId": "179000000000015",
"parentCommentId": 10,
"parentMetaId": "179000000000010",
"parentCommentText": "Great product!",
"text": "Thanks for the feedback!",
"createdAt": "2026-04-16T12:05:00Z",
"updatedAt": "2026-04-16T12:05:03Z",
"replyStatus": "sent",
"author": {
"type": "owner",
"name": "Support Agent"
}
},
"post": {
"id": 1,
"metaId": "179999999999999",
"text": "Reel description...",
"imageUrl": "https://dashboard.smsbat.com/api/meta/post/media/7d124c36-…",
"createdAt": "2026-07-22T09:35:30Z",
"mediaType": "reel"
}
}
6.5 Shared comment callback fields
Both comment callbacks share one body shape and differ only by type.
| Field | Description |
|---|---|
type | "new_comment" or "comment_status" |
platform | "facebook" or "instagram" |
comment.id | Internal comment ID |
comment.metaId | External ID in Meta; null for a pending reply before it is sent |
comment.parentCommentId | Parent comment ID. Absent for a top-level comment |
comment.parentMetaId | External parent comment ID. Absent at top level |
comment.parentCommentText | Parent comment text. Absent at top level |
comment.text | Comment text |
comment.createdAt | Creation date |
comment.updatedAt | Last update. Absent if the comment was never edited |
comment.replyStatus | "pending" / "sent" / "failure". Absent for an inbound user comment |
comment.author.type | "meta_user" or "owner" |
comment.author.name | Author name |
comment.author.metaUserId | Scoped author ID in Meta. Absent for "owner" |
comment.mediaUrl | Comment media. Absent when there is none |
post.id | Internal post ID |
post.metaId | External post / Reel / Story ID in Meta |
post.text | Post text |
post.imageUrl | Post image URL, or null |
post.createdAt | Post creation date |
post.mediaType | In comment callbacks, only post or reel |
6.6 New chat (source: 7)
{ "ChatId": 12345, "Phone": "+380501234567", "chat.Source": 7 }
6.7 Message and chat status changes (source: 6 / 5)
{ "name": "changed_message_status", "id": 9928, "status": 6 }
{ "name": "changed_chat_status", "id": 1867, "status": 4 }
6.8 Message edited or deleted (source: 9)
{ "messageId": 9930, "text": "Updated message text" }
{ "messageId": 9931, "delete": true }
6.9 Typing indicator (source: 8)
{ "chat_id": 1867, "event": "start_typing", "operator_name": "Iryna" }
{ "chat_id": 1867, "event": "end_typing" }
7. Event polling
For environments that cannot accept inbound HTTP.
7.1 Fetch events
GET https://chatapi.smsbat.com/api/chat/callback-events?page=1&perPage=20
Authorization: Bearer <token>
| Parameter | Description |
|---|---|
organizationId | Optional. Taken from the token when omitted |
page / perPage | Pagination, defaults 1 / 20 |
{
"page": 1,
"perPage": 20,
"total": 947,
"items": [
{
"ChatId": 1867,
"MessageId": 9928,
"MessageText": "Hello there!",
"MessageMedia": null,
"Phone": null,
"Username": "marianna_cat",
"UserId": 123,
"ShopId": 12,
"ShopName": "instagram shop name",
"Author": 1,
"type_messenger": 7,
"message_date": "2026-08-13T12:27:39.724Z",
"timestamp": "2026-08-13T12:27:39.736Z",
"event_guid": "ff60129e-c6e4-4876-9d90-badb430c0606",
"organization_id": 1,
"callback_type": "3"
}
]
}
Every event carries event_guid, timestamp, organization_id and callback_type — a
string matching the source values in §8.2. The remaining fields match the corresponding
webhook in §6.
7.2 Acknowledge processed events
POST https://chatapi.smsbat.com/api/chat/callback-events/processed
Authorization: Bearer <token>
Content-Type: application/json
[ "ff60129e-c6e4-4876-9d90-badb430c0606" ]
200 OK → { "deleted": 1 }
Events already removed simply do not count towards deleted. Ordering and retries are your
side’s responsibility.
7.3 Recommended loop
- Poll
GET /api/chat/callback-eventson a schedule. - Process the events in your service.
- Send the processed
event_guidlist to/callback-events/processed. - Repeat.
8. Enum reference
8.1 ChatSource — channel (0–9)
| Code | Channel |
|---|---|
| 0 | Viber |
| 1 | ViberBot |
| 2 | TelegramBot |
| 3 | |
| 4 | Widget |
| 5 | Rozetka |
| 6 | |
| 7 | |
| 8 | Prom |
| 9 | Olx |
8.2 SendingSourceCallback — callback event type (0–13)
| Code | Event |
|---|---|
| 3 | Chat — new chat message, including Story replies |
| 5 | Chat status changed |
| 6 | Message status changed |
| 7 | New chat created |
| 8 | Typing indicator |
| 9 | Message updated or deleted |
| 11 | AnyChatMessage — any chat message |
| 12 | MetaNewComment — new Instagram / Facebook comment |
| 13 | MetaCommentStatus — delivery status of our comment reply |
The enum spans 0–13; the remaining values are not needed for Instagram integrations.
8.3 ChatStatus (0–4)
0 New, 1 Open, 2 Waiting, 3 OnPause, 4 Closed
8.4 MessageStatus (0–11)
| Code | Name |
|---|---|
| 0 | NEW |
| 1 | SUCCESS |
| 2 | REJECTED |
| 3 | READ |
| 4 | UNKNOWN |
| 5 | PROCESSING |
| 6 | DELIVERED |
| 7 | BLOCKED_BY_USER |
| 8 | USER_NOT_FOUND |
The enum spans 0–11. Values 9, 10 and 11 exist in the API but are not yet documented —
treat them as UNKNOWN.
8.5 MediaType (1–10)
1 Photo, 2 File, 3 Audio, 4 Video, 5 Sticker, 6 StickerAnimated,
7 StickerVideo, 8 Animation, 9 Voice, 10 VideoNote
8.6 AuthorMessage — author in the Chat API (0–4)
0 Operator, 1 Client, 2 Bot, 3 ViberAccount
The enum spans 0–4; value 4 is undocumented. The “new message” callbacks use the
opposite mapping — see §6.2.
8.7 ChatMessageType (0–2)
0 Text, 1 Photo, 2 File
8.8 Comment replyStatus
null inbound user comment, "pending" our reply is queued, "sent" delivered,
"failure" delivery failed.
Open questions
Three points where the internal specification and the code-generated Swagger disagree. One request with a real token settles all of them; until then, write the client defensively.
| # | Question | Specification | Swagger | How to check |
|---|---|---|---|---|
| 1 | Auth header for /api/meta/* | X-Authorization-Key | only Bearer declared | curl -i -H "X-Authorization-Key: <token>" ".../api/meta/posts?perPage=1" — expect 200, not 401 |
| 2 | Counter field in Meta responses | totalCount | total | Same request — read the root JSON key |
| 3 | author.type type and reply status code | "meta_user" / "owner", 202 with body | int [0,1], 200 without body | curl -i .../api/meta/comments?perPage=1 plus a test reply |
Interim guidance:
- counter — read
total ?? totalCount; author.type— accept both a string and an integer (0↔meta_user,1↔owner, mapping to be confirmed);reply— treat any2xxas success, require no body, take the final status from thesource: 13callback.
Implementation notes
- Auth differs per endpoint group —
/api/meta/*usesX-Authorization-Key, chats and operators useBearer,restapiaccepts either. - Pagination is spelled two ways —
per_pageon/api/chat/chats,perPageon/api/meta/*and/api/chat/callback-events. multipart/form-datafields are PascalCase with dot notation (Media.File,Media.Type).- Null fields are omitted from callbacks — an absent key means
null. phoneis usuallynullon Instagram. Identify the customer byinstagramUser.id/metaUserIdand the shop byinstaAccount.id(theentityIdfilter value).Story.Idfrom a callback can be passed straight back asid/postIdto the Meta API.- Check the operator JWT’s
expiresAtbefore using it in a deeplink or the widget.