Help Center Meta & Instagram API Integration

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

PurposeURL
Chat API + Meta APIhttps://chatapi.smsbat.com
Swagger UI / OpenAPIhttps://chatapi.smsbat.com/index.html · …/swagger/v1/swagger.json
REST API (organizations, callback URLs)https://restapi.smsbat.com
REST API Swaggerhttps://restapi.smsbat.com/swagger/v1/swagger.json
Operator web panelhttps://chat.smsbat.com

2. Authentication

The auth scheme depends on the endpoint group. Mixing them up is the most common cause of 401.

GroupHeader
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>

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>
ParameterDescription
chat_raw_idChat ID
phonePhone number in international format
fromBrand / business account identifier (bm_id)
sourceChat source — 7 for Instagram, see §8.1
tokenValid, 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:

ParameterTypeDescription
sourceChatSource7 restricts results to Instagram
entityIdintBusiness account ID. Only applied together with source
instagram_user_idintInstagram user ID in ChatHub
facebook_user_idintFacebook user ID in ChatHub
page / per_pageintPagination, defaults 1 / 20
statusChatStatus[]Chat status, repeatable
searchstringFree-text search (name, phone, …)
organizationIdintOrganization ID
operatorIdint[]Filter by assigned operators
datestring[]Two bounds: ?date=…&date=…
isChainboolReturn chats as chains, carrying messages from previous chats
isUnread, starMark, isOperator, isAIAgentboolAdditional 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:

FieldMeaning
instaAccountThe Instagram business account (the shop). id is the entityId filter value; name is the account name from Meta
instagramUserThe customer. name is the Instagram handle, id is the instagram_user_id filter value
metaUserIdThe customer’s scoped ID on Meta’s side (string)
messSource7 for Instagram
phoneUsually 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
  }
}
FieldTypeDescription
textMessagestring?Message text. May be empty when media is present
authorAuthorMessage?0 operator, 1 client
isInternalbool?true marks an internal note that is not delivered to the customer
replyToMessageIdint?ID of the message being replied to
appGuiduuid?Referral GUID
mediaMediaDTO?{ 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 fieldTypeDescription
TextMessagestringMessage text
Authorint0 operator, 1 client
IsInternalboolInternal note
ReplyToMessageIdintMessage being replied to
AppGuiduuidReferral GUID
Media.FilebinaryThe file itself
Media.NamestringFile name
Media.FormatstringMIME type (video/mp4, image/png, application/pdf)
Media.TypeMediaTypeSee §8.5
Media.DataBase64stringAlternative to Media.File
Media.ThumbnailstringBase64 video preview frame
Media.DurationdoubleVideo 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>
ParameterTypeRequiredDescription
pageintnoPage, default 1
perPageintnoItems per page, default 20
idintnoFilter by internal post ID
platformstringnoinstagram or facebook
mediaTypestringnopost, 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.

FieldDescription
idInternal post ID
metaIdExternal post / Reel / Story ID in Meta
textPost caption
imageUrlProxy media URL keyed by the non-sequential MetaPost.Guid, or null
platformfacebook or instagram
mediaTypepost, reel or story
createdAtCreation date (platform date, or the database date)
storyPresent only for mediaType: "story"
story.idInternal Story ID; equal to post.id
story.metaIdExternal Story ID in Meta
story.urlStable 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>
ParameterTypeRequiredDescription
pageintnoPage, default 1
perPageintnoItems per page, default 20
postIdintnoFilter by post ID
parentCommentIdintnoChild comments (replies) of a given comment
platformstringnofacebook 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..."
      }
    }
  ]
}
FieldDescription
idInternal comment ID
metaIdExternal ID in Meta. null for a pending reply of ours until it is sent
textComment text
createdAtCreation date
platformfacebook or instagram
replyStatusnull for an inbound user comment; "pending" / "sent" / "failure" for our reply
author.type"meta_user" external user, "owner" page owner
author.nameAuthor name
author.metaUserIdScoped user ID in Meta; null for "owner"
postThe post, Reel or Story the comment belongs to
post.mediaTypepost, reel or story
post.storyStory reference { id, metaId, url }, Stories only
mediaUrlMedia attached to the comment, or null
replyToParent 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
  }'
FieldTypeDescription
urlstringYour endpoint
sourceSendingSourceCallbackEvent type, see §8.2
headerName / headerValuestringArbitrary auth header we attach to the request (optional)
channelTypeChatSourceChannel. 7 for Instagram. Optional
channelEntityIdintA 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"
  }
}
FieldDescription
ChatId / MessageIdChat and message identifiers
Author0 user, 1 operator
UsernameInstagram / Facebook display name or handle
UserIdInternal numeric user ID in SMSBAT
MetaUserIdScoped ID of the conversation partner in Meta. On an outgoing operator message this still identifies the Meta user of the chat, not the operator
ShopIdInternal ID of the Instagram / Facebook business account
ShopNameBusiness account name as received from Meta at connection time
MessageTextMessage text
MessageMediaMedia URL when the message is media
type_messengerSource, 7 for Instagram
operator_nameOperator name when Author = 1
StoryPresent only on an inbound Story reply
Story.IdInternal Story (MetaPost) ID — usable directly as id / postId in the Meta API
Story.MetaIdExternal Story ID in Meta
Story.UrlStable 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.

FieldDescription
type"new_comment" or "comment_status"
platform"facebook" or "instagram"
comment.idInternal comment ID
comment.metaIdExternal ID in Meta; null for a pending reply before it is sent
comment.parentCommentIdParent comment ID. Absent for a top-level comment
comment.parentMetaIdExternal parent comment ID. Absent at top level
comment.parentCommentTextParent comment text. Absent at top level
comment.textComment text
comment.createdAtCreation date
comment.updatedAtLast 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.nameAuthor name
comment.author.metaUserIdScoped author ID in Meta. Absent for "owner"
comment.mediaUrlComment media. Absent when there is none
post.idInternal post ID
post.metaIdExternal post / Reel / Story ID in Meta
post.textPost text
post.imageUrlPost image URL, or null
post.createdAtPost creation date
post.mediaTypeIn 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>
ParameterDescription
organizationIdOptional. Taken from the token when omitted
page / perPagePagination, 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.

  1. Poll GET /api/chat/callback-events on a schedule.
  2. Process the events in your service.
  3. Send the processed event_guid list to /callback-events/processed.
  4. Repeat.

8. Enum reference

8.1 ChatSource — channel (0–9)

CodeChannel
0Viber
1ViberBot
2TelegramBot
3Whatsapp
4Widget
5Rozetka
6Facebook
7Instagram
8Prom
9Olx

8.2 SendingSourceCallback — callback event type (0–13)

CodeEvent
3Chat — new chat message, including Story replies
5Chat status changed
6Message status changed
7New chat created
8Typing indicator
9Message updated or deleted
11AnyChatMessage — any chat message
12MetaNewComment — new Instagram / Facebook comment
13MetaCommentStatus — 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)

CodeName
0NEW
1SUCCESS
2REJECTED
3READ
4UNKNOWN
5PROCESSING
6DELIVERED
7BLOCKED_BY_USER
8USER_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.

#QuestionSpecificationSwaggerHow to check
1Auth header for /api/meta/*X-Authorization-Keyonly Bearer declaredcurl -i -H "X-Authorization-Key: <token>" ".../api/meta/posts?perPage=1" — expect 200, not 401
2Counter field in Meta responsestotalCounttotalSame request — read the root JSON key
3author.type type and reply status code"meta_user" / "owner", 202 with bodyint [0,1], 200 without bodycurl -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 any 2xx as success, require no body, take the final status from the source: 13 callback.

Implementation notes

  • Auth differs per endpoint group — /api/meta/* uses X-Authorization-Key, chats and operators use Bearer, restapi accepts either.
  • Pagination is spelled two ways — per_page on /api/chat/chats, perPage on /api/meta/* and /api/chat/callback-events.
  • multipart/form-data fields are PascalCase with dot notation (Media.File, Media.Type).
  • Null fields are omitted from callbacks — an absent key means null.
  • phone is usually null on Instagram. Identify the customer by instagramUser.id / metaUserId and the shop by instaAccount.id (the entityId filter value).
  • Story.Id from a callback can be passed straight back as id / postId to the Meta API.
  • Check the operator JWT’s expiresAt before using it in a deeplink or the widget.