Pager API · v2
API Documentation
Pull conversations, messages and clients into your own systems, get notified about new messages and comments via webhooks, send messages and files to clients, reply to comments, and manage conversations, managers' access and Pager's reference data.
The Pager API gives your systems access to your organization's data. You can read conversations, messages and clients, send messages and files to clients, reply to Instagram comments, change a conversation's status, responsible manager and group, fill in client cards, manage managers' access to channels, and manage the reference data managers use in the dashboard: statuses, client groups, saved reply folders and saved replies. Pager can also notify your server about new messages and comments on its own via webhooks.
https://api.pager.co.ua/v2- Requests and responses are UTF-8 JSON; dates are ISO 8601 in UTC.
- Every list comes in the same envelope,
{ data, hasMore, nextCursor }, so you write the pagination loop once — see Pagination. - Unknown fields in the body or query aren't ignored: they return
400 unknown_parameter, so a typo in a field name never slips by silently. - A machine-readable API description is available as an OpenAPI spec: for Postman, client generation and AI assistants.
- Every response has an
X-Request-Idheader. Include it when you contact support. - Every request is signed with an API key — see Authentication.
Send your organization's API key in the Authorization header as Bearer <key>. A key belongs to one organization: the API only reads and changes that organization's data.
Keys look like pgr_live_ followed by 40 characters. A missing, invalid, revoked or expired key always gets the same response: 401 invalid_api_key.
Start your integration with GET /me: it confirms the key works and returns your organization, plan and rate limits.
curl "https://api.pager.co.ua/v2/me" \
-H "Authorization: Bearer $PAGER_API_KEY"Conversations, messages, clients and conversation history come in pages. Each page is an envelope: { data, hasMore, nextCursor }.
limitis the page size, from1to100,50by default.- If
hasMoreistrue, passnextCursoras thecursorparameter to get the next page. Keep all other parameters the same. - Records go from newest to oldest. Pages never duplicate or skip records, even when several records share the same timestamp.
- The cursor is opaque: don't parse or build it yourself. A malformed cursor returns
400 invalid_cursor. - Reference data (statuses, groups, folders, saved replies) and a conversation's orders are returned in full:
hasMoreis alwaysfalse.
# First page
curl "https://api.pager.co.ua/v2/conversations?limit=100" \
-H "Authorization: Bearer $PAGER_API_KEY"
# Next page: nextCursor from the previous response
curl "https://api.pager.co.ua/v2/conversations?limit=100&cursor=WyIyMDI2LTA5LTIzVDE2OjAyOjExLjg0MFoiLCIyYjljNGQ2ZS04ZjBhLTRjMmUtOWI0ZC02ZjhhMGMyZTRiNmQiXQ" \
-H "Authorization: Bearer $PAGER_API_KEY"Syncing
updatedSince instead of a full walk. The conversation list is sorted by lastMessageAt: a conversation that gets a new message jumps to the top, so a page-by-page walk can miss it. Remember the largest updatedAt you've received and pass it as updatedSince next time. The bound is inclusive, so the last records come again — upsert by id rather than inserting.Lists accept filters in the query string. Different filters combine with AND: a record must match each of them.
- Several values for one filter:
?statusId=a,bor?statusId=a&statusId=b. Any of the values matches. nullmeans an empty field, same as in the dashboard:?responsibleUserId=null— conversations with no responsible manager,?statusId=a,null— with statusaor no status. Works forstatusId,responsibleUserIdandclientGroupId.qis a case-insensitive search over the same fields as the dashboard search. Each resource lists its fields in the parameter description.- An ID from another organization in a filter isn't an error — the list is just empty.
- An unknown parameter returns
400 unknown_parameter.
expand
Without expand, a conversation only carries the IDs of related objects (clientId, statusId and so on), so lists stay lean. The expand parameter adds the objects themselves: client, channel, status, clientGroup, responsibleUser, comma-separated. An unknown value returns 400 with param: "expand". Channel tokens and keys are never included.
curl "https://api.pager.co.ua/v2/conversations?statusId=5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e,null&responsibleUserId=null&expand=client,status" \
-H "Authorization: Bearer $PAGER_API_KEY"Errors come with a matching HTTP status and an error object. Handle them by type, the broad category; code narrows down the cause, param points to the request field, and requestId matches the X-Request-Id header.
{
"error": {
"type": "invalid_request",
"code": "invalid_parameter",
"message": "Color must be #rrggbb",
"param": "color",
"requestId": "req_4f1c9a2e7b3d4c6e9a0f1d2e3f4a5b6c"
}
}| HTTP | type | When it happens |
|---|---|---|
400 | invalid_request | A field failed validation (invalid_parameter), an unknown field was sent (unknown_parameter), the body isn't valid JSON (invalid_body), or an ID in the body doesn't belong to your organization. Webhooks also use invalid_webhook_url and webhook_limit_reached — see Limits. |
401 | authentication_error | The key is missing, invalid, revoked or expired (invalid_api_key). |
402 | quota_exceeded | The plan has expired or the message balance is exhausted (message_limit_reached). Returned when sending a message. |
403 | permission_error | The key isn't allowed to perform this action. |
404 | not_found | The object isn't in your organization (status_not_found and so on), or the route doesn't exist (route_not_found). |
409 | conflict | The request conflicts with the current state: the externalId is taken by another client (external_id_taken), the idempotency key was already used (idempotency_key_reused, idempotency_request_in_progress), the comment already has a private reply (private_reply_exists), or a delivery of a disabled webhook can't be retried (webhook_disabled). |
422 | channel_error | The message couldn't be sent to the channel: the messenger refused it (channel_rejected), the channel doesn't support sending (channel_unsupported), or it isn't configured — see Send a message. |
429 | rate_limit_exceeded | Rate limit exceeded — see Rate limits. |
500 | api_error | Internal Pager error. Retry later, and include requestId when contacting support. |
Another organization's object returns the same 404 as a non-existent one — the API never confirms that it exists.
The permission_error type is reserved for future use. Rely on type and code, not the message text: it may change.
Limits are tracked per key using a leaky bucket. Each request adds its cost to the bucket, and the bucket drains at a steady rate. While the cost fits, the request goes through; when it doesn't, the API returns 429 rate_limit_exceeded.
| Bucket | Capacity | Drains | |
|---|---|---|---|
general | 60 | 10 per second | All requests. |
outbound | 30 | 1 per second | Requests that go out to messengers: each sent message, comment reply and comment deletion takes an extra 1. So up to 30 such requests in a burst, then one per second. |
Request cost: 1 to retrieve, create, update or delete an object, 2 for a page of a list, 5 for an organization-wide message search or a conversation count. Each operation shows its cost next to it. In practice, you can burst up to 60 single-cost requests, then sustain 10 per second.
GET /me returns the key's current limits in rateLimits.
| Header | When it happens |
|---|---|
RateLimit-Limit | Bucket capacity. |
RateLimit-Remaining | How many units still fit in the bucket. |
RateLimit-Reset | Seconds until the bucket is fully drained. |
RateLimit-Policy | The policy as 60;w=6: capacity and how many seconds a full bucket takes to drain. |
Retry-After | On 429 responses only: seconds to wait before retrying. |
HTTP/1.1 429 Too Many Requests
RateLimit-Limit: 60
RateLimit-Remaining: 0
RateLimit-Reset: 6
RateLimit-Policy: 60;w=6
Retry-After: 1429, wait the number of seconds in Retry-After and retry. Retrying without a pause will just hit the limit again.Requests with side effects accept an Idempotency-Key header. If the connection dropped and you don't know whether the request went through, retry it with the same key — Pager won't perform the action twice and returns the stored response instead.
curl -X POST "https://api.pager.co.ua/v2/conversations/2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d/messages" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Idempotency-Key: a3f1c9e2-5b7d-4e8a-9c0b-1d2e3f4a5b6c" \
-H "Content-Type: application/json" \
-d '{"text":"Дякуємо за замовлення! Номер ТТН: 20450000012345"}'- For now, sending a message, replying to a comment and creating a webhook subscription accept the header; other requests ignore it. The header is optional: without it the request runs as usual.
- A key is any string up to 255 characters, unique for each new action. A UUID is the easiest choice. An empty or too long key returns
400 invalid_idempotency_key. - A key is scoped to your organization and lives for 24 hours. A retry with the same key and the same body gets the stored response with an
Idempotent-Replayed: trueheader. - Only successful (
2xx) responses are stored. After an error the key is released, so you can send a corrected request with the same key. - The same key with a different body or path returns
409 idempotency_key_reused. While the first request is still running —409 idempotency_request_in_progress: wait and retry.
A file is sent to a client in three steps: get an upload link, upload the file to Pager's storage, and send a message with the ready-made attachment. The file goes to storage directly, bypassing the API, so its size doesn't affect rate limits.
- 1
POST /files with the
conversationId, name, MIME type and size of the file. The response containsuploadUrl, theheadersto use and a ready-madeattachmentobject. - 2
Within 5 minutes, upload the file with a
PUTrequest touploadUrlusing the headers fromheaders. NoAuthorizationhere: the signature in the link grants access. - 3
Send a message with
attachments: [attachment]— the object from step one, unchanged.
# 1. Upload link
curl -X POST "https://api.pager.co.ua/v2/files" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d", "name": "invoice-184502.pdf", "mime": "application/pdf", "size": 248193}'
# 2. The file goes straight to storage, no Authorization
curl -X PUT "$UPLOAD_URL" \
-H "Content-Type: application/pdf" \
--data-binary @invoice-184502.pdf
# 3. Message with the attachment
curl -X POST "https://api.pager.co.ua/v2/conversations/2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d/messages" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"attachments": [ATTACHMENT]}'One message carries either text or one file: most messengers drop text sent next to a file. To send a file with a caption, send two messages.
Up to 20 MB. Images, video, audio, PDF, ZIP, Office documents, TXT and CSV are allowed — see Files. If the file wasn't uploaded, the API returns 400 attachment_not_uploaded; if the uploaded file is larger than allowed — 400 attachment_too_large.
The full API description is available in OpenAPI 3.1 format. The spec is generated from the same schemas the API uses to validate requests, so it always matches the current version. Import it into Postman or Insomnia, or generate a client for your language from it.
https://api.pager.co.ua/v2/openapi.json
https://api.pager.co.ua/v2/docsGET /v2/openapi.json— the spec: every endpoint, parameter, object and error schema, the cost of each request, plus the webhook event formats in thewebhookssection.GET /v2/docs— interactive docs built from the spec: browse the schemas and send requests with your key right from the browser.- Both URLs are public: no API key is needed, and requests to them don't count towards rate limits. The response is cached for 5 minutes.
# Download the spec
curl -o pager-openapi.json https://api.pager.co.ua/v2/openapi.jsonAn organization admin creates the key in the Pager dashboard. An organization can have only one active key.
- 1
Open Settings → API.
- 2
Click Create key and give it a name, e.g. “CRM integration”. The name is just for you, to remember where the key is used.
- 3
Copy the key and store it somewhere safe. The full key is shown only once: Pager stores only its hash, so a lost key can't be recovered — only reissued.
Reissue the key if it may have leaked, or if someone who had access to it has left the team.
- 1
In Settings → API, click Reissue.
- 2
Change the name if needed and confirm.
- 3
Save the new key and replace it in all your integrations.
401, so update your integrations right after reissuing.If two admins reissue the key at the same time, one of the requests is rejected. Refresh the page and check which key is active now.
Revoke the key when the integration is no longer needed. The organization then has no active key until you create a new one.
- 1
In Settings → API, click the trash button next to the key.
- 2
Confirm the revocation.
401 invalid_api_key.Webhooks notify your system about events in Pager as soon as they happen: Pager sends a POST request with the event to your URL. No need to poll the API for new messages.
- A subscription is a URL plus the list of events to send to it. An organization can have up to 10 subscriptions, for example one for your CRM and another for analytics.
- Every request is signed with the subscription's secret, so your server can make sure the event really came from Pager — see Verifying signatures.
- If your server is down, Pager keeps retrying for about 10 hours — see Delivery and retries.
- Subscriptions can be managed in the dashboard or via the API — see the Webhook subscriptions resource. A subscription created in the dashboard is visible via the API, and vice versa.
First, prepare a public HTTPS endpoint on your server that accepts POST requests with JSON. Then an organization admin creates the subscription in the dashboard.
- 1
Open Settings → API → Webhooks and click Add webhook.
- 2
Enter the endpoint URL and select the events to subscribe to. The description is optional: it only helps you remember where the events go.
- 3
Copy the signing secret (
whsec_…) and store it on your server, for example in thePAGER_WEBHOOK_SECRETenvironment variable. The secret is shown only once: it can't be recovered, only rotated. - 4
In the subscription menu, open Deliveries & test and click Send test. Pager immediately sends a
webhook.testevent and shows what your server responded.
Via the API, a subscription is created with POST /webhooks: the secret comes in the secret field of the response. Check the endpoint with POST /webhooks/{id}/test.
A new subscription receives only events that happen after it was created. It doesn't get the message history: to load existing data, walk the API lists — see Pagination.
Each event arrives in a separate request. The request body is a JSON envelope of the same shape for every event type, with the event data in the data field.
| Event | When it happens |
|---|---|
message.received | A client sent a message to any of the organization's channels. |
message.sent | The organization sent a message to a client: a manager in the dashboard, your system via the API, a broadcast or an automation. |
comment.received | A client left a comment under an Instagram post — a new one or a reply in a thread. Replies on behalf of the page are not events. |
webhook.test | A test event from the Send test button or POST /webhooks/{id}/test. You can't subscribe to it: it's only sent on request. data.message is a string. |
Event envelope
| Field | When it happens |
|---|---|
id | Event ID, evt_…. It stays the same across retries and matches the Pager-Event-Id header, which makes it the key for dropping duplicates. |
type | Event type — see the table above. |
createdAt | When the event happened, ISO 8601 in UTC. |
organizationId | The organization the event happened in. |
data | Event data. For message events: message, conversation, client and channel; for comment.received, comment instead of message. |
{
"id": "evt_6c1f0e9d8b7a45c3a2e1f0d9c8b7a6e5",
"type": "message.received",
"createdAt": "2026-09-24T08:31:40.582Z",
"organizationId": "org_2rRfXLYNflNpMgzPBX1MU18z39C",
"data": {
"message": {
"id": "d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"direction": "incoming",
"text": "А доставка у Львів скільки йде?",
"attachments": [],
"authorId": null,
"replyToMessageId": null,
"externalId": "aWdfZAG1faXRlbTo…",
"reaction": null,
"isRead": false,
"isEdited": false,
"isDelivered": null,
"errorMessage": null,
"ad": null,
"storyReplyUrl": null,
"createdAt": "2026-09-24T08:31:40.117Z",
"updatedAt": "2026-09-24T08:31:40.117Z"
},
"conversation": {
"id": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"clientId": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"statusId": "5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e",
"responsibleUserId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"clientGroupId": "1c3e5a7b-9d2f-4b6c-8e0a-2f4d6b8a0c1e",
"state": "unread",
"lastMessageDirection": "incoming",
"lastMessageAt": "2026-09-24T08:31:40.117Z",
"snippet": "А доставка у Львів скільки йде?",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-24T08:31:40.117Z"
},
"client": {
"id": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"externalId": null,
"name": "Олена Коваль",
"username": "olena.koval",
"imageUrl": "https://files.pager.co.ua/…/avatar.jpg",
"phone": null,
"infoName": "Олена",
"infoLastName": "Коваль",
"infoPhone": "+380671234567",
"infoEmail": "olena@example.com",
"infoAddress": "Київ, НП №52",
"infoNote": "Цікавиться оптовими цінами",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-23T15:02:11.840Z"
},
"channel": {
"id": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"name": "Instagram магазину",
"channelSource": "Instagram",
"username": "kvitka.shop",
"imageUrl": "https://files.pager.co.ua/…/channel.jpg"
}
}
}{
"id": "evt_3e2d1c0b9a8f47e6d5c4b3a2f1e0d9c8",
"type": "comment.received",
"createdAt": "2026-09-24T10:02:16.204Z",
"organizationId": "org_2rRfXLYNflNpMgzPBX1MU18z39C",
"data": {
"comment": {
"id": "6d5c4b3a-2f1e-4d0c-9b8a-7f6e5d4c3b2a",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"parentId": null,
"direction": "incoming",
"text": "Скільки коштує доставка у Львів?",
"username": "olena.koval",
"authorId": null,
"media": {
"id": "18045678901234567",
"url": "https://www.instagram.com/p/C9xYz1AbCdE/"
},
"hasPrivateReply": false,
"createdAt": "2026-09-24T10:02:15.000Z",
"updatedAt": "2026-09-24T10:02:15.000Z"
},
"conversation": {
"id": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"clientId": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"statusId": "5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e",
"responsibleUserId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"clientGroupId": "1c3e5a7b-9d2f-4b6c-8e0a-2f4d6b8a0c1e",
"state": "unread",
"lastMessageDirection": "incoming",
"lastMessageAt": "2026-09-24T08:31:40.117Z",
"snippet": "А доставка у Львів скільки йде?",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-24T08:31:40.117Z"
},
"client": {
"id": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"externalId": null,
"name": "Олена Коваль",
"username": "olena.koval",
"imageUrl": "https://files.pager.co.ua/…/avatar.jpg",
"phone": null,
"infoName": "Олена",
"infoLastName": "Коваль",
"infoPhone": "+380671234567",
"infoEmail": "olena@example.com",
"infoAddress": "Київ, НП №52",
"infoNote": "Цікавиться оптовими цінами",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-23T15:02:11.840Z"
},
"channel": {
"id": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"name": "Instagram магазину",
"channelSource": "Instagram",
"username": "kvitka.shop",
"imageUrl": "https://files.pager.co.ua/…/channel.jpg"
}
}
}In message.* events the objects use the same formats as the REST API: message, conversation without expand, and client. channel is the channel: id, name, channelSource, username, imageUrl. The objects are a snapshot at the time of the event.
In comment.received, data.comment is a comment without replies. For a client's reply in a thread, parentId points to the thread root. Reply with POST /conversations/{id}/comments.
Links in message.attachments[].url are temporary: they're signed at the moment of each delivery attempt, and expiresAt shows when they expire. If you need the file, download it right away.
Delivery order is not guaranteed: events are sent in parallel and retries shift them in time. Use message.createdAt to order messages.
Expect new fields to appear in the objects: don't reject an event because of an unknown field.
Pager signs every request with the subscription's secret. Verify the signature before trusting an event: a webhook URL is not a secret, and anyone can send a request to it.
POST /pager/webhook HTTP/1.1
Host: crm.example.com
Content-Type: application/json
User-Agent: Pager-Webhooks/1.0
Pager-Event-Id: evt_6c1f0e9d8b7a45c3a2e1f0d9c8b7a6e5
Pager-Event-Type: message.received
Pager-Delivery: dlv_4b7e1f0a-9c2d-4e8b-a6f3-5d1c0b9e8a72
Pager-Attempt: 1
Pager-Signature: t=1758702700,v1=5f0c8a1e2b7d4c9f3a6e1b8d0c7f2a5e9b4d1c8f7a2e6b3d0c9f5a8e1b4d7c2a| Header | When it happens |
|---|---|
Pager-Signature | t=<unix time>,v1=<signature>, where v1 is the hex HMAC-SHA256 of the string <t>.<request body>, keyed with the subscription's secret. t is the time of that particular attempt, so a retry has a new one. |
Pager-Event-Id | The event id. Same across all retries. |
Pager-Event-Type | Event type, same as type in the body. |
Pager-Delivery | Delivery ID, dlv_… — the same id as in the delivery log. |
Pager-Attempt | Attempt number, starting at 1. |
- 1
Take the
tandv1values from thePager-Signatureheader. - 2
Compute the HMAC-SHA256 of the string
<t>.<body>with your secret. Use the raw body, byte for byte as it arrived: after parsing and re-serializing the JSON the signature won't match. - 3
Compare the result with
v1using a constant-time comparison (crypto.timingSafeEqual,hmac.compare_digest). - 4
Reject requests whose
tdiffers from the current time by more than 5 minutes, so an intercepted request can't be replayed later.
import crypto from "node:crypto"
import express from "express"
const app = express()
const seen = new Set()
// The signature covers the raw body: don't parse JSON before verifying
app.post("/pager/webhook", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("Pager-Signature") ?? ""
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")))
// 1. Expected signature: HMAC-SHA256 of "t.body"
const expected = crypto
.createHmac("sha256", process.env.PAGER_WEBHOOK_SECRET)
.update(`${t}.${req.body}`)
.digest("hex")
// 2. Constant-time comparison
const valid =
typeof v1 === "string" &&
v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))
// 3. No older than 5 minutes
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 5 * 60
if (!valid || !fresh) return res.status(400).end()
const event = JSON.parse(req.body)
// 4. Retries carry the same event.id: process each event once
if (!seen.has(event.id)) {
seen.add(event.id)
queue.push(event)
}
// 5. Respond 2xx right away and process the event in the background
res.status(200).send("ok")
})A delivery succeeds if your server responds with any 2xx code within 10 seconds. Pager doesn't interpret the response body, it only stores its beginning in the log.
- Anything else is a failure: another status code,
3xx(redirects aren't followed), a timeout or a connection error. Pager retries on the schedule below. - Respond as fast as you can: put the event in a queue, return
200, and process it afterwards. Long processing inside the request leads to timeouts and unnecessary retries. - Events are delivered at least once: the same event may arrive twice, for example if your response got lost in the network. Drop duplicates by the event
id(Pager-Event-Id). - After the last failed attempt the delivery becomes
deadand is no longer retried automatically. You can retry it manually in the dashboard or via the API.
Retry schedule
| Attempt | When |
|---|---|
| 1 | right after the event |
| 2 | +10 s |
| 3 | +30 s |
| 4 | +1 min |
| 5 | +5 min |
| 6 | +15 min |
| 7 | +1 h |
| 8 | +3 h |
| 9 | +6 h |
Each delay counts from the previous attempt and varies by ±20%, so subscriptions that failed together don't come back in a single wave. In total, up to 9 attempts over about 10 hours.
Automatic disabling
If no delivery to the subscription's URL succeeds for 7 days in a row, Pager disables the subscription: enabled becomes false and disabledReason becomes delivery_failures. Pager doesn't send an email about it: the state is visible in the dashboard and via GET /webhooks/{id}.
While a failure streak lasts, failingSince shows when it started and consecutiveFailures counts failed attempts in a row. The first successful delivery resets both. Test events don't count towards the streak.
To turn the subscription back on, click Enable in the dashboard or send enabled: true to PATCH /webhooks/{id}. The failure streak is reset, and deliveries still in the queue continue.
The limits protect both your server and Pager: from excess load and from requests into internal networks.
- Up to 10 subscriptions per organization. Creating one more returns
400 webhook_limit_reached. https://only. URLs up to 2000 characters, with no username or password in the address.- The URL must point to a public address. Local and private addresses (
localhost,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,169.254.0.0/16and so on) are rejected with400 invalid_webhook_url. The address is checked when the subscription is saved and again before every delivery, so the DNS record can't be swapped after the subscription is created. - Redirects aren't followed: a
3xxresponse counts as a failure. Use the final URL. - The timeout is 10 seconds for the whole request, including reading the response.
- Only the first 2 KB of the response body go into the log.
- Subscription description — up to 500 characters.
- Available events:
message.received,message.sentandcomment.received.
Details about your organization and the key that signed the request: plan, remaining messages, key mask and rate limits.
Response fields
organizationobjectThe organization's
id,nameandtimezone. The time zone defaults toEurope/Kyiv.planobject | nullCurrent plan:
name,maxUsers,maxChannelsandendDate— the date it's paid through.nullif there's no plan.messagesobject | nullRemaining messages:
includedfrom the plan,extrapurchased on top;resetAtis when the plan's allowance renews.apiKeyobjectThe request's key:
name, mask (prefix+last4),scopes,createdAtandexpiresAt. The full key is never returned.rateLimitsobjectCapacity and drain rate of each bucket — see Rate limits.
Retrieve organization
/v2/mecost 1The best first request for an integration: a 200 means the key works.
curl "https://api.pager.co.ua/v2/me" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"organization": {
"id": "org_2rRfXLYNflNpMgzPBX1MU18z39C",
"name": "Магазин «Квітка»",
"timezone": "Europe/Kyiv"
},
"plan": {
"id": "b1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f",
"name": "Business",
"maxUsers": 10,
"maxChannels": 5,
"endDate": "2026-10-24T00:00:00.000Z"
},
"messages": {
"included": 18240,
"extra": 1500,
"resetAt": "2026-10-01T00:00:00.000Z"
},
"apiKey": {
"name": "Інтеграція з CRM",
"prefix": "pgr_live_",
"last4": "1JgQ",
"scopes": [
"*"
],
"createdAt": "2026-09-12T09:03:44.000Z",
"expiresAt": null
},
"rateLimits": {
"general": {
"capacity": 60,
"ratePerSecond": 10
},
"outbound": {
"capacity": 30,
"ratePerSecond": 1
}
}
}A conversation is the thread with one client in one channel. The list is sorted by lastMessageAt, newest first. Besides reading, you can change a conversation's status, responsible manager, group and read state, and send messages into it.
- Conversations with the
SPAMstatus aren't hidden, unlike in the dashboard. If you don't need them, pass the statuses you want instatusId(plusnullfor conversations with no status). - A conversation with a new message moves to the top of the list. For syncing, use
updatedSince— see Pagination.
The conversation object
idstringConversation ID.
channelIdstringThe channel the conversation is in.
clientIdstringThe conversation's client — see Clients.
statusIdstring | nullStatus, or
nullif not set.responsibleUserIdstring | nullResponsible manager, or
null.clientGroupIdstring | nullClient group, or
null.stateenumunread— there are unread incoming messages,read— all read.lastMessageDirectionenum | nullWho wrote last:
incoming— the client,outgoing— your organization.lastMessageAtISO 8601Time of the last message.
snippetstringText of the last message, for previews.
createdAtISO 8601When the conversation was created.
updatedAtISO 8601When the conversation was last changed.
clientobject · expandThe client object. Only with
expand=client.channelobject · expandChannel:
id,name,channelSource,username,imageUrl. Only withexpand=channel.statusobject · expandThe status object. Only with
expand=status.clientGroupobject · expandThe client group object. Only with
expand=clientGroup.responsibleUserobject · expandManager:
id,firstName,lastName,imageUrl. Only withexpand=responsibleUser.
{
"id": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"clientId": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"statusId": "5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e",
"responsibleUserId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"clientGroupId": "1c3e5a7b-9d2f-4b6c-8e0a-2f4d6b8a0c1e",
"state": "unread",
"lastMessageDirection": "incoming",
"lastMessageAt": "2026-09-24T08:31:40.117Z",
"snippet": "А доставка у Львів скільки йде?",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-24T08:31:40.117Z"
}List conversations
/v2/conversationscost 2Returns conversations page by page. The example below fetches unread conversations with no responsible manager, together with their client.
Parameters
channelIdstring[]queryoptionalOnly conversations in these channels.
statusIdstring[]queryoptionalOnly conversations with these statuses.
null— conversations with no status.responsibleUserIdstring[]queryoptionalOnly conversations of these managers.
null— conversations with no responsible manager.clientGroupIdstring[]queryoptionalOnly conversations in these groups.
null— conversations with no group.stateenumqueryoptionalreadorunread.directionenumqueryoptionalWho wrote last:
incoming— the client (awaiting a reply),outgoing— your organization.updatedSinceISO 8601queryoptionalOnly conversations changed since this moment. ISO 8601 with a time zone.
qstringqueryoptionalSearches the conversation ID, last message text, and the client's name, username, phone, email and note, plus Zoho ID.
limitintegerqueryoptionalPage size, from
1to100. Defaults to50.cursorstringqueryoptionalnextCursorfrom the previous page.expandstringqueryoptionalRelated objects, comma-separated:
client,channel,status,clientGroup,responsibleUser.
curl "https://api.pager.co.ua/v2/conversations?state=unread&responsibleUserId=null&limit=20&expand=client" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"id": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"clientId": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"statusId": "5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e",
"responsibleUserId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"clientGroupId": "1c3e5a7b-9d2f-4b6c-8e0a-2f4d6b8a0c1e",
"state": "unread",
"lastMessageDirection": "incoming",
"lastMessageAt": "2026-09-24T08:31:40.117Z",
"snippet": "А доставка у Львів скільки йде?",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-24T08:31:40.117Z",
"client": {
"id": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"externalId": null,
"name": "Олена Коваль",
"username": "olena.koval",
"imageUrl": "https://files.pager.co.ua/…/avatar.jpg",
"phone": null,
"infoName": "Олена",
"infoLastName": "Коваль",
"infoPhone": "+380671234567",
"infoEmail": "olena@example.com",
"infoAddress": "Київ, НП №52",
"infoNote": "Цікавиться оптовими цінами",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-23T15:02:11.840Z"
}
}
],
"hasMore": true,
"nextCursor": "WyIyMDI2LTA5LTIzVDE2OjAyOjExLjg0MFoiLCIyYjljNGQ2ZS04ZjBhLTRjMmUtOWI0ZC02ZjhhMGMyZTRiNmQiXQ"
}Count conversations
/v2/conversations/countcost 5Takes the same filters as the list and returns just { count } — for an unread counter on a dashboard, say.
Parameters
channelIdstring[]queryoptionalOnly conversations in these channels.
statusIdstring[]queryoptionalOnly conversations with these statuses.
null— conversations with no status.responsibleUserIdstring[]queryoptionalOnly conversations of these managers.
null— conversations with no responsible manager.clientGroupIdstring[]queryoptionalOnly conversations in these groups.
null— conversations with no group.stateenumqueryoptionalreadorunread.directionenumqueryoptionalWho wrote last:
incoming— the client (awaiting a reply),outgoing— your organization.updatedSinceISO 8601queryoptionalOnly conversations changed since this moment. ISO 8601 with a time zone.
qstringqueryoptionalSearches the conversation ID, last message text, and the client's name, username, phone, email and note, plus Zoho ID.
curl "https://api.pager.co.ua/v2/conversations/count?state=unread&responsibleUserId=null" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"count": 17
}Retrieve a conversation
/v2/conversations/{id}cost 1Returns one conversation. Accepts expand.
Parameters
idstringpathrequiredConversation ID.
expandstringqueryoptionalRelated objects, comma-separated:
client,channel,status,clientGroup,responsibleUser.
curl "https://api.pager.co.ua/v2/conversations/2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d?expand=client,status,responsibleUser" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"id": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"clientId": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"statusId": "5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e",
"responsibleUserId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"clientGroupId": "1c3e5a7b-9d2f-4b6c-8e0a-2f4d6b8a0c1e",
"state": "unread",
"lastMessageDirection": "incoming",
"lastMessageAt": "2026-09-24T08:31:40.117Z",
"snippet": "А доставка у Львів скільки йде?",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-24T08:31:40.117Z",
"client": {
"id": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"externalId": null,
"name": "Олена Коваль",
"username": "olena.koval",
"imageUrl": "https://files.pager.co.ua/…/avatar.jpg",
"phone": null,
"infoName": "Олена",
"infoLastName": "Коваль",
"infoPhone": "+380671234567",
"infoEmail": "olena@example.com",
"infoAddress": "Київ, НП №52",
"infoNote": "Цікавиться оптовими цінами",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-23T15:02:11.840Z"
},
"status": {
"id": "5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e",
"name": "В роботі",
"sortIndex": 0,
"systemStatus": "IN_PROGRESS"
},
"responsibleUser": {
"id": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"firstName": "Ірина",
"lastName": "Шевчук",
"imageUrl": null
}
}Update a conversation
/v2/conversations/{id}cost 1Changes a conversation's status, responsible manager, group or read state. Only the fields you send change; null clears a field. Accepts expand, like retrieving a conversation.
Parameters
idstringpathrequiredConversation ID.
expandstringqueryoptionalRelated objects, comma-separated:
client,channel,status,clientGroup,responsibleUser.statusIdstring | nullbodyoptionalNew status, or
nullto clear it.responsibleUserIdstring | nullbodyoptionalNew responsible manager — a member of your organization — or
nullto unassign. Manager IDs are in conversations'responsibleUserIdand inexpand=responsibleUser.clientGroupIdstring | nullbodyoptionalNew group, or
nullto clear it.stateenumbodyoptionalread— mark as read,unread— mark as unread.
- Status, group and manager IDs must belong to your organization, otherwise the API returns
400with the matchingparam. - Status and responsible manager changes go into the conversation history with
userId: null. - A body with no fields returns
400.
curl -X PATCH "https://api.pager.co.ua/v2/conversations/2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d?expand=status" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"statusId":"7c9e1a3b-5d7f-4a2c-8e6b-0d2f4a6c8e1b","state":"read"}'{
"id": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"clientId": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"statusId": "7c9e1a3b-5d7f-4a2c-8e6b-0d2f4a6c8e1b",
"responsibleUserId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"clientGroupId": "1c3e5a7b-9d2f-4b6c-8e0a-2f4d6b8a0c1e",
"state": "read",
"lastMessageDirection": "incoming",
"lastMessageAt": "2026-09-24T08:31:40.117Z",
"snippet": "А доставка у Львів скільки йде?",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-24T09:02:17.408Z",
"status": {
"id": "7c9e1a3b-5d7f-4a2c-8e6b-0d2f4a6c8e1b",
"name": "Оплачено",
"sortIndex": 3,
"systemStatus": "COMPLETED"
}
}Conversation history
/v2/conversations/{id}/historycost 2Status and responsible manager changes in one feed, newest first, page by page.
Parameters
idstringpathrequiredConversation ID.
limitintegerqueryoptionalPage size, from
1to100. Defaults to50.cursorstringqueryoptionalnextCursorfrom the previous page.
typeisstatus_changed(fieldsoldStatusId,newStatusId) orresponsible_changed(fieldsoldResponsibleUserId,newResponsibleUserId).userIdis who made the change.nullmeans it was made via the API or by automation (a broadcast, for example), not by a manager in the dashboard.
curl "https://api.pager.co.ua/v2/conversations/2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d/history" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"id": "f0e1d2c3-b4a5-4968-8776-655443322110",
"type": "status_changed",
"userId": null,
"oldStatusId": "7c9e1a3b-5d7f-4a2c-8e6b-0d2f4a6c8e1b",
"newStatusId": "5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e",
"createdAt": "2026-09-24T08:40:03.221Z"
},
{
"id": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
"type": "responsible_changed",
"userId": "user_2rRfXLYNflNpMgzPBX1MU18z39C",
"oldResponsibleUserId": null,
"newResponsibleUserId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"createdAt": "2026-09-23T16:01:12.650Z"
}
],
"hasMore": false,
"nextCursor": null
}Conversation orders
/v2/conversations/{id}/orderscost 2Orders placed from this conversation, newest first. Returned in full, without pagination.
Parameters
idstringpathrequiredConversation ID.
amountis the order total, an integer.crmis where the order was sent:LP_CRM,ZOHOornull;externalIdis the order number in that CRM.userIdis the manager who placed the order.
curl "https://api.pager.co.ua/v2/conversations/2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d/orders" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"id": "0c1d2e3f-4a5b-4c6d-8e7f-9a0b1c2d3e4f",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"userId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"amount": 1450,
"externalId": "184502",
"crm": "LP_CRM",
"createdAt": "2026-09-23T16:20:31.000Z",
"updatedAt": "2026-09-23T16:20:31.000Z"
}
],
"hasMore": false,
"nextCursor": null
}Conversation messages
/v2/conversations/{id}/messagescost 2A conversation's messages page by page, newest first. Format: the message object.
Parameters
idstringpathrequiredConversation ID.
limitintegerqueryoptionalPage size, from
1to100. Defaults to50.cursorstringqueryoptionalnextCursorfrom the previous page.updatedSinceISO 8601queryoptionalOnly messages changed since this moment (new, edited, or with a changed delivery status or reaction).
curl "https://api.pager.co.ua/v2/conversations/2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d/messages?limit=2" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"id": "d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"direction": "incoming",
"text": "А доставка у Львів скільки йде?",
"attachments": [],
"authorId": null,
"replyToMessageId": null,
"externalId": "aWdfZAG1faXRlbTo…",
"reaction": null,
"isRead": false,
"isEdited": false,
"isDelivered": null,
"errorMessage": null,
"ad": null,
"storyReplyUrl": null,
"createdAt": "2026-09-24T08:31:40.117Z",
"updatedAt": "2026-09-24T08:31:40.117Z"
},
{
"id": "c9d8e7f6-a5b4-4c3d-9e2f-1a0b9c8d7e6f",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"direction": "outgoing",
"text": "Відправляємо Новою поштою по всій Україні, доставка 1–2 дні.",
"attachments": [],
"authorId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"replyToMessageId": null,
"externalId": "aWdfZAG1faXRlbTo…",
"reaction": null,
"isRead": true,
"isEdited": false,
"isDelivered": true,
"errorMessage": null,
"ad": null,
"storyReplyUrl": null,
"createdAt": "2026-09-23T16:05:02.004Z",
"updatedAt": "2026-09-23T16:05:02.004Z"
}
],
"hasMore": true,
"nextCursor": "WyIyMDI2LTA5LTI0VDA4OjMxOjQwLjExN1oiLCJkMWUyZjNhNC1iNWM2LTRkN2UtOGY5YS0wYjFjMmQzZTRmNWEiXQ"
}Send a message
/v2/conversations/{id}/messagescost 1outbound 1Sends text or a file to the client on behalf of your organization — through the conversation's channel, just like a manager does from the dashboard. Returns the stored message with a 201.
Parameters
idstringpathrequiredConversation ID.
Idempotency-KeystringheaderoptionalA key that makes a retry not send the message twice — see Idempotency.
textstringbodyoptionalMessage text, 1 to 4096 characters. Not sent together with
attachments.attachmentsobject[]bodyoptionalAn array with one attachment — the
attachmentobject from POST /files, unchanged. An attachment from another conversation or your own file URL returns400 invalid_attachment.replyToMessageIdstringbodyoptionalA message in this conversation you're replying to. A message from another conversation returns
400.
- Pass either
textorattachmentswith one file — not both, otherwise400. For uploading a file, see Sending files. - Text is up to 4096 characters. Some messengers have a lower limit — then the refusal comes back as
422. authorIdin the response isnull: the message was sent via the API, not by a manager.- Send an
Idempotency-Key: if the response got lost to a timeout, a retry with the same key won't message the client twice. - The plan is checked before sending: if it has expired or the message balance is exhausted —
402 message_limit_reached. A sent message counts towards the allowance. - Sending works for Instagram, Facebook Messenger, Telegram bots, personal Telegram, Viber and WhatsApp via e-chat, website chat and custom channels. Other channels return
422 channel_unsupported. - If the messenger refuses (for example, too much time has passed since the client's last message on Instagram or Facebook), the API returns
422 channel_rejected. The message is still saved as undelivered — managers see it in the dashboard, and itsidis in the error text. - After sending, the conversation becomes read and moves to the top of the list.
- Besides a cost of
1in thegeneralbucket, it takes1from theoutboundbucket — see Rate limits.
curl -X POST "https://api.pager.co.ua/v2/conversations/2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d/messages" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Idempotency-Key: a3f1c9e2-5b7d-4e8a-9c0b-1d2e3f4a5b6c" \
-H "Content-Type: application/json" \
-d '{"text":"Доставка у Львів — 1–2 дні, від 80 грн. Оформити замовлення?","replyToMessageId":"d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a"}'{
"id": "e5f6a7b8-c9d0-4e1f-a2b3-c4d5e6f7a8b9",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"direction": "outgoing",
"text": "Доставка у Львів — 1–2 дні, від 80 грн. Оформити замовлення?",
"attachments": [],
"authorId": null,
"replyToMessageId": "d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a",
"externalId": "aWdfZAG1faXRlbTo…",
"reaction": null,
"isRead": false,
"isEdited": false,
"isDelivered": true,
"errorMessage": null,
"ad": null,
"storyReplyUrl": null,
"createdAt": "2026-09-24T08:34:02.771Z",
"updatedAt": "2026-09-24T08:34:02.771Z"
}Conversation messages. To read a thread, use conversation messages; this resource is for organization-wide search and retrieving a single message.
- An organization-wide search is heavier than reading one conversation, so it costs
5. If you know the conversation, read its messages instead. attachments[].urllinks are temporary; they expire atexpiresAt.
The message object
idstringMessage ID.
conversationIdstringThe conversation the message belongs to.
directionenumincoming— from the client,outgoing— from your organization.textstring | nullText, or
nullif the message only has attachments.attachmentsobject[]Attachments:
type(image,video,audioordocument),url,name,mime,sizein bytes andexpiresAt.authorIdstring | nullThe manager who sent the message.
nullfor incoming messages, and for outgoing ones sent via the API, a broadcast or automation.replyToMessageIdstring | nullThe message this one replies to, or
null.externalIdstring | nullThe message ID in the messenger.
reactionstring | nullA reaction to the message, such as an emoji, or
null.isReadbooleanWhether the message has been read.
isEditedbooleanWhether the message was edited.
isDeliveredboolean | nulltrue/false— the messenger's delivery report.null— the messenger doesn't report status (Viber and WhatsApp via e-chat), or the message is incoming.errorMessagestring | nullWhy the message wasn't delivered, or
null.adobject | nullThe ad the client wrote from:
id,url,text.nullif the message didn't come from an ad.storyReplyUrlstring | nullThe story the client replied to, or
null.createdAtISO 8601When the message was sent.
updatedAtISO 8601When the message was last changed.
{
"id": "c9d8e7f6-a5b4-4c3d-9e2f-1a0b9c8d7e6f",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"direction": "outgoing",
"text": "Відправляємо Новою поштою по всій Україні, доставка 1–2 дні.",
"attachments": [],
"authorId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"replyToMessageId": null,
"externalId": "aWdfZAG1faXRlbTo…",
"reaction": null,
"isRead": true,
"isEdited": false,
"isDelivered": true,
"errorMessage": null,
"ad": null,
"storyReplyUrl": null,
"createdAt": "2026-09-23T16:05:02.004Z",
"updatedAt": "2026-09-23T16:05:02.004Z"
}Search messages
/v2/messagescost 5Searches messages across all of your organization's conversations, newest first. The example below finds incoming messages since September that mention “розмір” (size).
Parameters
qstringqueryoptionalCase-insensitive search in message text.
channelIdstring[]queryoptionalOnly messages from conversations in these channels.
directionenumqueryoptionalincoming— from clients,outgoing— from your organization.fromISO 8601queryoptionalStart of the period by
createdAt, inclusive. ISO 8601.toISO 8601queryoptionalEnd of the period by
createdAt, inclusive. ISO 8601.updatedSinceISO 8601queryoptionalOnly messages changed since this moment.
limitintegerqueryoptionalPage size, from
1to100. Defaults to50.cursorstringqueryoptionalnextCursorfrom the previous page.
curl "https://api.pager.co.ua/v2/messages?q=розмір&direction=incoming&from=2026-09-01T00:00:00Z" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"id": "7a6b5c4d-3e2f-4a1b-8c9d-0e1f2a3b4c5d",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"direction": "incoming",
"text": "Доброго дня! Чи є в наявності розмір M?",
"attachments": [],
"authorId": null,
"replyToMessageId": null,
"externalId": "aWdfZAG1faXRlbTo…",
"reaction": null,
"isRead": false,
"isEdited": false,
"isDelivered": null,
"errorMessage": null,
"ad": {
"id": "120214567890123456",
"url": "https://fb.me/…",
"text": "Осіння колекція −20%"
},
"storyReplyUrl": null,
"createdAt": "2026-09-23T15:58:44.910Z",
"updatedAt": "2026-09-23T15:58:44.910Z"
}
],
"hasMore": false,
"nextCursor": null
}Retrieve a message
/v2/messages/{id}cost 1Returns one message.
Parameters
idstringpathrequiredMessage ID.
curl "https://api.pager.co.ua/v2/messages/d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"id": "d1e2f3a4-b5c6-4d7e-8f9a-0b1c2d3e4f5a",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"direction": "incoming",
"text": "А доставка у Львів скільки йде?",
"attachments": [],
"authorId": null,
"replyToMessageId": null,
"externalId": "aWdfZAG1faXRlbTo…",
"reaction": null,
"isRead": false,
"isEdited": false,
"isDelivered": null,
"errorMessage": null,
"ad": null,
"storyReplyUrl": null,
"createdAt": "2026-09-24T08:31:40.117Z",
"updatedAt": "2026-09-24T08:31:40.117Z"
}Uploading a file to Pager's storage so you can send it to a client. The API doesn't accept the file directly: POST /files returns a temporary link to upload the file to, and a ready-made attachment object for the message. Step by step — see Sending files.
- Up to 20 MB. Images, video and audio (except SVG) are allowed, plus PDF, ZIP, DOC/DOCX, XLS/XLSX, PPT/PPTX, TXT and CSV. Other types return
400withparam: "mime". - The file is bound to the conversation in
conversationId: it can only be sent to that conversation.
Response fields
uploadUrlstringTemporary link for uploading the file.
methodstringHTTP method for the upload —
PUT.headersobjectHeaders to send with the upload, verbatim.
Content-Typeis part of the link's signature.expiresInintegerHow many seconds the link is valid —
300.expiresAtISO 8601The moment by which the upload has to start.
maxBytesintegerMaximum file size in bytes.
attachmentobjectA ready-made attachment: after the upload, pass this object unchanged in
attachmentswhen sending a message.
Get an upload link
/v2/filescost 1Returns an upload link and a ready-made attachment object with 201.
Parameters
conversationIdstringbodyrequiredThe conversation you'll send the file to.
namestringbodyrequiredFile name, up to 255 characters. The client sees it in the messenger.
mimestringbodyrequiredThe file's MIME type, e.g.
application/pdforimage/jpeg.sizeintegerbodyrequiredFile size in bytes, up to 20 MB.
- The link is valid for 5 minutes — that's the time to start the upload. A slow upload of a large file won't be cut off.
- Upload with a
PUTrequest touploadUrlusing the headers fromheaders, withoutAuthorization. The request body is the file itself. - Size and type are checked again when the message is sent — against the file actually uploaded.
curl -X POST "https://api.pager.co.ua/v2/files" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"conversationId":"2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d","name":"invoice-184502.pdf","mime":"application/pdf","size":248193}'{
"uploadUrl": "https://files.pager.co.ua/…/7f3e2d1c-0b9a-4c8d-9e7f-6a5b4c3d2e1f?X-Amz-Expires=300&X-Amz-Signature=…",
"method": "PUT",
"headers": {
"Content-Type": "application/pdf"
},
"expiresIn": 300,
"maxBytes": 20971520,
"attachment": {
"type": "document",
"payload": {
"key": "org_2rRfXLYNflNpMgzPBX1MU18z39C/2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d/7f3e2d1c-0b9a-4c8d-9e7f-6a5b4c3d2e1f",
"name": "invoice-184502.pdf",
"mime": "application/pdf",
"size": 248193
}
},
"expiresAt": "2026-09-24T08:44:12.000Z"
}A client is a person who wrote to one of your organization's channels. Each client has one conversation. The list is sorted by creation date, newest first. You can fill in the client card via the API.
phoneis the client's number in the messenger (Telegram, Viber, WhatsApp). Theinfo*fields are the client card filled in by a manager or a CRM.- Internal platform IDs (PSID, Telegram ID) are never returned.
The client object
idstringClient ID.
channelIdstringThe channel the client wrote to.
conversationIdstring | nullThe conversation with the client.
externalIdstring | nullThe client's ID in an external system, or
null.namestring | nullName from the messenger profile.
usernamestring | nullMessenger username.
imageUrlstring | nullAvatar.
phonestring | nullNumber in the messenger, if known.
infoNamestring | nullFirst name on the client card.
infoLastNamestring | nullLast name on the client card.
infoPhonestring | nullPhone on the client card.
infoEmailstring | nullEmail on the client card.
infoAddressstring | nullAddress on the client card.
infoNotestring | nullManager's note.
createdAtISO 8601When the client first wrote.
updatedAtISO 8601When the client's data was last changed.
{
"id": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"externalId": null,
"name": "Олена Коваль",
"username": "olena.koval",
"imageUrl": "https://files.pager.co.ua/…/avatar.jpg",
"phone": null,
"infoName": "Олена",
"infoLastName": "Коваль",
"infoPhone": "+380671234567",
"infoEmail": "olena@example.com",
"infoAddress": "Київ, НП №52",
"infoNote": "Цікавиться оптовими цінами",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-23T15:02:11.840Z"
}List clients
/v2/clientscost 2Returns clients page by page. The example below searches by phone number.
Parameters
channelIdstring[]queryoptionalOnly clients from these channels.
clientGroupIdstring[]queryoptionalOnly clients whose conversation is in these groups.
null— clients with no group.updatedSinceISO 8601queryoptionalOnly clients changed since this moment.
qstringqueryoptionalSearches name, username, external ID, messenger phone numbers and card fields: first name, last name, phone, email.
limitintegerqueryoptionalPage size, from
1to100. Defaults to50.cursorstringqueryoptionalnextCursorfrom the previous page.
curl "https://api.pager.co.ua/v2/clients?q=0671234567" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"id": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"externalId": null,
"name": "Олена Коваль",
"username": "olena.koval",
"imageUrl": "https://files.pager.co.ua/…/avatar.jpg",
"phone": null,
"infoName": "Олена",
"infoLastName": "Коваль",
"infoPhone": "+380671234567",
"infoEmail": "olena@example.com",
"infoAddress": "Київ, НП №52",
"infoNote": "Цікавиться оптовими цінами",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-23T15:02:11.840Z"
}
],
"hasMore": false,
"nextCursor": null
}Retrieve a client
/v2/clients/{id}cost 1Returns one client.
Parameters
idstringpathrequiredClient ID.
curl "https://api.pager.co.ua/v2/clients/3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"id": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"externalId": null,
"name": "Олена Коваль",
"username": "olena.koval",
"imageUrl": "https://files.pager.co.ua/…/avatar.jpg",
"phone": null,
"infoName": "Олена",
"infoLastName": "Коваль",
"infoPhone": "+380671234567",
"infoEmail": "olena@example.com",
"infoAddress": "Київ, НП №52",
"infoNote": "Цікавиться оптовими цінами",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-23T15:02:11.840Z"
}Update a client
/v2/clients/{id}cost 1Fills in the client card. Only the fields you send change; null clears a field, and surrounding whitespace is trimmed.
Parameters
idstringpathrequiredClient ID.
infoNamestring | nullbodyoptionalFirst name on the card, up to 255 characters.
infoLastNamestring | nullbodyoptionalLast name on the card, up to 255 characters.
infoPhonestring | nullbodyoptionalPhone on the card, up to 50 characters.
infoEmailstring | nullbodyoptionalEmail on the card. Must be a valid address, otherwise
400.infoAddressstring | nullbodyoptionalAddress on the card, up to 500 characters.
infoNotestring | nullbodyoptionalNote, up to 8000 characters.
externalIdstring | nullbodyoptionalThe client's ID in your system, up to 255 characters, unique within the channel.
- If the Zoho CRM integration is connected and the first name, last name, phone or email changed, the card is synced to Zoho — same as after an edit in the dashboard.
- An
externalIdtaken by another client in this channel returns409 external_id_taken. - For custom channel clients,
externalIdis the ID Pager uses to find the client and deliver replies. Only change it if the ID on your platform changed. - A body with no fields returns
400.
curl -X PATCH "https://api.pager.co.ua/v2/clients/3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"infoAddress":"Львів, НП №12","infoNote":"Оптовий клієнт, знижка 10%","externalId":"crm-10482"}'{
"id": "3b0f6c1e-5d2a-4f7e-9c41-2a8e7d9b1f03",
"channelId": "e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"conversationId": "2b9c4d6e-8f0a-4c2e-9b4d-6f8a0c2e4b6d",
"externalId": "crm-10482",
"name": "Олена Коваль",
"username": "olena.koval",
"imageUrl": "https://files.pager.co.ua/…/avatar.jpg",
"phone": null,
"infoName": "Олена",
"infoLastName": "Коваль",
"infoPhone": "+380671234567",
"infoEmail": "olena@example.com",
"infoAddress": "Львів, НП №12",
"infoNote": "Оптовий клієнт, знижка 10%",
"createdAt": "2026-08-14T09:21:37.512Z",
"updatedAt": "2026-09-24T09:05:41.132Z"
}The organization's managers and admins. userId is the same ID as in conversations' responsibleUserId, messages' authorId and history's userId. Via the API you can read members and change which channels they see.
- Inviting or removing members and changing their role is only possible in the dashboard. A
rolefield inPATCHreturns400 unknown_parameter. - Returned in full, without pagination, in the order they joined the organization.
Member object
userIdstringUser ID,
user_….firstNamestring | nullFirst name, or
null.lastNamestring | nullLast name, or
null.emailsstring[]The user's email addresses.
imageUrlstring | nullAvatar, or
null.rolestringRole in the organization:
org:admin— admin,org:member— manager.accessibleChannelsstring[]Channels whose conversations the member sees in the dashboard. Conversations from other channels are hidden from them.
createdAtISO 8601When the user signed up to Pager.
{
"userId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"firstName": "Ірина",
"lastName": "Шевчук",
"emails": [
"iryna@kvitka.shop"
],
"imageUrl": null,
"role": "org:member",
"accessibleChannels": [
"e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a"
],
"createdAt": "2026-08-02T09:40:11.000Z"
}List members
/v2/memberscost 2Returns all members of the organization.
curl "https://api.pager.co.ua/v2/members" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"userId": "user_2rRfXLYNflNpMgzPBX1MU18z39C",
"firstName": "Андрій",
"lastName": "Мельник",
"emails": [
"owner@kvitka.shop"
],
"imageUrl": "https://img.clerk.com/…",
"role": "org:admin",
"accessibleChannels": [
"e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d"
],
"createdAt": "2026-07-18T12:03:27.000Z"
},
{
"userId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"firstName": "Ірина",
"lastName": "Шевчук",
"emails": [
"iryna@kvitka.shop"
],
"imageUrl": null,
"role": "org:member",
"accessibleChannels": [
"e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a"
],
"createdAt": "2026-08-02T09:40:11.000Z"
}
],
"hasMore": false,
"nextCursor": null
}Retrieve a member
/v2/members/{userId}cost 1Returns a single member.
Parameters
userIdstringpathrequiredUser ID,
user_….
curl "https://api.pager.co.ua/v2/members/user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"userId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"firstName": "Ірина",
"lastName": "Шевчук",
"emails": [
"iryna@kvitka.shop"
],
"imageUrl": null,
"role": "org:member",
"accessibleChannels": [
"e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a"
],
"createdAt": "2026-08-02T09:40:11.000Z"
}Change channel access
/v2/members/{userId}cost 1Replaces the list of channels the member sees and returns the updated member.
Parameters
userIdstringpathrequiredUser ID,
user_….accessibleChannelsstring[]bodyrequiredThe full new list of channels, up to 500. The member stops seeing channels not in the list; an empty array hides all channels.
- This is a full replacement, not an addition: to open one more channel, send the current list plus that channel.
- Every channel must belong to your organization, otherwise
400 channel_not_foundwithparam: "accessibleChannels". Duplicates are removed. - Channel IDs are available in the
channelIdof conversations and clients.
curl -X PATCH "https://api.pager.co.ua/v2/members/user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"accessibleChannels":["e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a","a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d"]}'{
"userId": "user_2sKq8vB3nT1mZ7wYcR4dF6hJ9pL",
"firstName": "Ірина",
"lastName": "Шевчук",
"emails": [
"iryna@kvitka.shop"
],
"imageUrl": null,
"role": "org:member",
"accessibleChannels": [
"e4b2a1c9-6d8f-4e3a-b7c5-0f9e8d7c6b5a",
"a7b8c9d0-e1f2-4a3b-8c4d-5e6f7a8b9c0d"
],
"createdAt": "2026-08-02T09:40:11.000Z"
}Conversation statuses — the stages managers mark conversations with in the dashboard. Returned in sortIndex order, same as in the dashboard.
- Deleting a status doesn't delete conversations: they're left without a status, just like when you delete it in the dashboard.
The status object
idstringStatus ID.
namestringName, up to 100 characters.
sortIndexintegerPosition in the list, from
0.systemStatusenum | nullSystem category:
IN_PROGRESS,CONSIDERING,COMPLETED,DECLINED,SPAM, ornullfor a regular status.
{
"id": "5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e",
"name": "В роботі",
"sortIndex": 0,
"systemStatus": "IN_PROGRESS"
}List statuses
/v2/statusescost 2Returns all of your organization's statuses.
curl "https://api.pager.co.ua/v2/statuses" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"id": "5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e",
"name": "В роботі",
"sortIndex": 0,
"systemStatus": "IN_PROGRESS"
}
],
"hasMore": false,
"nextCursor": null
}Create a status
/v2/statusescost 1Creates a status and returns it with a 201.
Parameters
namestringbodyrequiredStatus name, 1 to 100 characters.
sortIndexintegerbodyoptionalPosition in the list, from
0. If omitted on create, the status goes last.systemStatusenum | nullbodyoptionalSystem category (
IN_PROGRESS,CONSIDERING,COMPLETED,DECLINED,SPAM) ornull.
curl -X POST "https://api.pager.co.ua/v2/statuses" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Чекає на оплату","systemStatus":"CONSIDERING"}'{
"id": "5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e",
"name": "Чекає на оплату",
"sortIndex": 0,
"systemStatus": "CONSIDERING"
}Update a status
/v2/statuses/{id}cost 1Changes only the fields you send. A body with no fields returns 400.
Parameters
idstringpathrequiredStatus ID.
namestringbodyoptionalStatus name, 1 to 100 characters.
sortIndexintegerbodyoptionalPosition in the list, from
0. If omitted on create, the status goes last.systemStatusenum | nullbodyoptionalSystem category (
IN_PROGRESS,CONSIDERING,COMPLETED,DECLINED,SPAM) ornull.
curl -X PATCH "https://api.pager.co.ua/v2/statuses/5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Оплачено","systemStatus":"COMPLETED"}'{
"id": "5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e",
"name": "Оплачено",
"sortIndex": 0,
"systemStatus": "COMPLETED"
}Delete a status
/v2/statuses/{id}cost 1Deletes the status and responds with 204. Its conversations are left without a status.
Parameters
idstringpathrequiredStatus ID.
curl -X DELETE "https://api.pager.co.ua/v2/statuses/5a7e9c1d-3f2b-4d8a-b6e0-9c1f2a3b4d5e" \
-H "Authorization: Bearer $PAGER_API_KEY"Groups managers use to tag clients in the dashboard, like “Wholesale” or “VIP”. Returned in sortIndex order.
- Deleting a group doesn't delete conversations: they're left without a group, just like when you delete it in the dashboard.
The client group object
idstringGroup ID.
namestringName, up to 100 characters.
colorstringColor as
#rrggbb— exactly how the dashboard stores it.sortIndexintegerPosition in the list, from
0.
{
"id": "1c3e5a7b-9d2f-4b6c-8e0a-2f4d6b8a0c1e",
"name": "Опт",
"color": "#7c5cff",
"sortIndex": 0
}List client groups
/v2/client-groupscost 2Returns all of your organization's client groups.
curl "https://api.pager.co.ua/v2/client-groups" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"id": "1c3e5a7b-9d2f-4b6c-8e0a-2f4d6b8a0c1e",
"name": "Опт",
"color": "#7c5cff",
"sortIndex": 0
}
],
"hasMore": false,
"nextCursor": null
}Create a client group
/v2/client-groupscost 1Creates a group and returns it with a 201.
Parameters
namestringbodyrequiredGroup name, 1 to 100 characters.
colorstringbodyrequiredColor as
#rrggbb, e.g.#7c5cff. Other formats (red,#fff,rgb(…)) return400.sortIndexintegerbodyoptionalPosition in the list, from
0. If omitted on create, the group goes last.
curl -X POST "https://api.pager.co.ua/v2/client-groups" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"VIP","color":"#f59e0b"}'{
"id": "1c3e5a7b-9d2f-4b6c-8e0a-2f4d6b8a0c1e",
"name": "VIP",
"color": "#f59e0b",
"sortIndex": 0
}Update a client group
/v2/client-groups/{id}cost 1Changes only the fields you send. A body with no fields returns 400.
Parameters
idstringpathrequiredGroup ID.
namestringbodyoptionalGroup name, 1 to 100 characters.
colorstringbodyoptionalColor as
#rrggbb, e.g.#7c5cff. Other formats (red,#fff,rgb(…)) return400.sortIndexintegerbodyoptionalPosition in the list, from
0. If omitted on create, the group goes last.
curl -X PATCH "https://api.pager.co.ua/v2/client-groups/1c3e5a7b-9d2f-4b6c-8e0a-2f4d6b8a0c1e" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"color":"#16a34a"}'{
"id": "1c3e5a7b-9d2f-4b6c-8e0a-2f4d6b8a0c1e",
"name": "Опт",
"color": "#16a34a",
"sortIndex": 0
}Delete a client group
/v2/client-groups/{id}cost 1Deletes the group and responds with 204. Its conversations are left without a group.
Parameters
idstringpathrequiredGroup ID.
curl -X DELETE "https://api.pager.co.ua/v2/client-groups/1c3e5a7b-9d2f-4b6c-8e0a-2f4d6b8a0c1e" \
-H "Authorization: Bearer $PAGER_API_KEY"Folders that group saved replies. Returned in sortIndex order.
- Deleting a folder also deletes all of its saved replies, just like in the dashboard. They can't be restored.
updatedSincereturns only folders changed since that moment — handy for syncing. Deleted folders aren't in the response: compare against the full list to spot them.
The folder object
idstringFolder ID.
namestringName, up to 100 characters.
sortIndexintegerPosition in the list, from
0.createdAtISO 8601When the folder was created.
updatedAtISO 8601When the folder was last changed.
{
"id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
"name": "Доставка",
"sortIndex": 0,
"createdAt": "2026-08-02T10:15:00.000Z",
"updatedAt": "2026-09-20T08:41:27.000Z"
}List folders
/v2/saved-reply-folderscost 2Returns your organization's saved reply folders.
Parameters
updatedSinceISO 8601queryoptionalReturn only folders updated since this moment. ISO 8601 with a time zone, e.g.
2026-09-01T00:00:00Z.
curl "https://api.pager.co.ua/v2/saved-reply-folders?updatedSince=2026-09-01T00:00:00Z" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
"name": "Доставка",
"sortIndex": 0,
"createdAt": "2026-08-02T10:15:00.000Z",
"updatedAt": "2026-09-20T08:41:27.000Z"
}
],
"hasMore": false,
"nextCursor": null
}Create a folder
/v2/saved-reply-folderscost 1Creates an empty folder and returns it with a 201.
Parameters
namestringbodyrequiredFolder name, 1 to 100 characters.
sortIndexintegerbodyoptionalPosition in the list, from
0. If omitted on create, the folder goes last.
curl -X POST "https://api.pager.co.ua/v2/saved-reply-folders" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Оплата"}'{
"id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
"name": "Оплата",
"sortIndex": 0,
"createdAt": "2026-08-02T10:15:00.000Z",
"updatedAt": "2026-09-20T08:41:27.000Z"
}Update a folder
/v2/saved-reply-folders/{id}cost 1Changes only the fields you send. A body with no fields returns 400.
Parameters
idstringpathrequiredFolder ID.
namestringbodyoptionalFolder name, 1 to 100 characters.
sortIndexintegerbodyoptionalPosition in the list, from
0. If omitted on create, the folder goes last.
curl -X PATCH "https://api.pager.co.ua/v2/saved-reply-folders/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"name":"Доставка й оплата"}'{
"id": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
"name": "Доставка й оплата",
"sortIndex": 0,
"createdAt": "2026-08-02T10:15:00.000Z",
"updatedAt": "2026-09-20T08:41:27.000Z"
}Delete a folder
/v2/saved-reply-folders/{id}cost 1Deletes the folder together with all of its saved replies and responds with 204.
Parameters
idstringpathrequiredFolder ID.
curl -X DELETE "https://api.pager.co.ua/v2/saved-reply-folders/9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b" \
-H "Authorization: Bearer $PAGER_API_KEY"Ready-made replies managers insert into a conversation in one click. Returned grouped by folder, in sortIndex order.
folderIdmust belong to your organization, otherwise the API returns400withparam: "folderId".- Attachments are read-only for now: you can't add or change them via the API.
attachments[].urllinks are temporary; they expire atexpiresAt. Don't store them — fetch the saved reply again instead.
The saved reply object
idstringSaved reply ID.
folderIdstringThe folder the reply is in.
textstringReply text, up to 8000 characters.
sortIndexintegerPosition within the folder, from
0.attachmentsobject[]Attachments:
type(image,video,audioordocument),url,name,mime,sizein bytes andexpiresAt.createdAtISO 8601When the reply was created.
updatedAtISO 8601When the reply was last changed.
{
"id": "3f2e1d0c-9b8a-4f7e-a6d5-c4b3a2f1e0d9",
"folderId": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
"text": "Відправляємо Новою поштою по всій Україні, доставка 1–2 дні.",
"sortIndex": 0,
"attachments": [
{
"type": "image",
"url": "https://files.pager.co.ua/…/tariffs.png?X-Amz-Expires=3600&…",
"name": "tariffs.png",
"mime": "image/png",
"size": 184320,
"expiresAt": "2026-09-24T13:00:00.000Z"
}
],
"createdAt": "2026-08-02T10:16:12.000Z",
"updatedAt": "2026-09-20T08:41:27.000Z"
}List saved replies
/v2/saved-repliescost 2Returns your organization's saved replies.
Parameters
folderIdstringqueryoptionalReturn only replies from this folder.
updatedSinceISO 8601queryoptionalReturn only replies updated since this moment. ISO 8601 with a time zone.
curl "https://api.pager.co.ua/v2/saved-replies?folderId=9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"id": "3f2e1d0c-9b8a-4f7e-a6d5-c4b3a2f1e0d9",
"folderId": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
"text": "Відправляємо Новою поштою по всій Україні, доставка 1–2 дні.",
"sortIndex": 0,
"attachments": [
{
"type": "image",
"url": "https://files.pager.co.ua/…/tariffs.png?X-Amz-Expires=3600&…",
"name": "tariffs.png",
"mime": "image/png",
"size": 184320,
"expiresAt": "2026-09-24T13:00:00.000Z"
}
],
"createdAt": "2026-08-02T10:16:12.000Z",
"updatedAt": "2026-09-20T08:41:27.000Z"
}
],
"hasMore": false,
"nextCursor": null
}Create a saved reply
/v2/saved-repliescost 1Creates a reply in a folder and returns it with a 201.
Parameters
folderIdstringbodyrequiredThe reply's folder. On update, moves the reply to another folder.
textstringbodyrequiredReply text, 1 to 8000 characters.
sortIndexintegerbodyoptionalPosition within the folder, from
0. If omitted on create, the reply goes last in the folder.
curl -X POST "https://api.pager.co.ua/v2/saved-replies" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"folderId":"9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b","text":"Оплата при отриманні або на картку ФОП."}'{
"id": "3f2e1d0c-9b8a-4f7e-a6d5-c4b3a2f1e0d9",
"folderId": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
"text": "Оплата при отриманні або на картку ФОП.",
"sortIndex": 0,
"attachments": [],
"createdAt": "2026-08-02T10:16:12.000Z",
"updatedAt": "2026-09-20T08:41:27.000Z"
}Update a saved reply
/v2/saved-replies/{id}cost 1Changes only the fields you send. To move the reply to another folder, send a new folderId.
Parameters
idstringpathrequiredSaved reply ID.
folderIdstringbodyoptionalThe reply's folder. On update, moves the reply to another folder.
textstringbodyoptionalReply text, 1 to 8000 characters.
sortIndexintegerbodyoptionalPosition within the folder, from
0. If omitted on create, the reply goes last in the folder.
curl -X PATCH "https://api.pager.co.ua/v2/saved-replies/3f2e1d0c-9b8a-4f7e-a6d5-c4b3a2f1e0d9" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"text":"Оплата при отриманні, на картку ФОП або через Apple Pay."}'{
"id": "3f2e1d0c-9b8a-4f7e-a6d5-c4b3a2f1e0d9",
"folderId": "9e8d7c6b-5a4f-4e3d-8c2b-1a0f9e8d7c6b",
"text": "Оплата при отриманні, на картку ФОП або через Apple Pay.",
"sortIndex": 0,
"attachments": [
{
"type": "image",
"url": "https://files.pager.co.ua/…/tariffs.png?X-Amz-Expires=3600&…",
"name": "tariffs.png",
"mime": "image/png",
"size": 184320,
"expiresAt": "2026-09-24T13:00:00.000Z"
}
],
"createdAt": "2026-08-02T10:16:12.000Z",
"updatedAt": "2026-09-20T08:41:27.000Z"
}Delete a saved reply
/v2/saved-replies/{id}cost 1Deletes the reply and responds with 204.
Parameters
idstringpathrequiredSaved reply ID.
curl -X DELETE "https://api.pager.co.ua/v2/saved-replies/3f2e1d0c-9b8a-4f7e-a6d5-c4b3a2f1e0d9" \
-H "Authorization: Bearer $PAGER_API_KEY"Subscriptions Pager sends events to, and their delivery log. It's the same list as Settings → API → Webhooks in the dashboard. For receiving and verifying events, see Webhooks.
- The signing secret is returned only in responses to creating a subscription and rotating the secret. Other responses never include it.
- Another organization's subscription returns
404 webhook_not_found, same as a nonexistent one.
Subscription object
idstringSubscription ID.
urlstringThe address events are sent to.
eventsenum[]Subscribed events:
message.received,message.sent,comment.received.descriptionstring | nullA note for yourself, or
null.enabledbooleanWhether events are sent to this subscription.
consecutiveFailuresintegerFailed delivery attempts in a row. Reset by the first successful delivery.
failingSinceISO 8601 | nullStart of the current failure streak, or
null. After 7 days of failures the subscription is disabled automatically — see Delivery and retries.disabledAtISO 8601 | nullWhen the subscription was disabled, or
nullif it's enabled.disabledReasonenum | nullmanual— disabled by hand in the dashboard or via the API,delivery_failures— automatically after 7 days of failures,null— the subscription is enabled.createdAtISO 8601When the subscription was created.
updatedAtISO 8601When the subscription was last changed.
{
"id": "8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b",
"url": "https://crm.example.com/pager/webhook",
"events": [
"message.received",
"message.sent"
],
"description": "Синхронізація з CRM",
"enabled": true,
"consecutiveFailures": 0,
"failingSince": null,
"disabledAt": null,
"disabledReason": null,
"createdAt": "2026-09-24T09:12:05.301Z",
"updatedAt": "2026-09-24T09:12:05.301Z"
}List subscriptions
/v2/webhookscost 2Returns all of the organization's subscriptions at once, without pagination.
curl "https://api.pager.co.ua/v2/webhooks" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"id": "8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b",
"url": "https://crm.example.com/pager/webhook",
"events": [
"message.received",
"message.sent"
],
"description": "Синхронізація з CRM",
"enabled": true,
"consecutiveFailures": 0,
"failingSince": null,
"disabledAt": null,
"disabledReason": null,
"createdAt": "2026-09-24T09:12:05.301Z",
"updatedAt": "2026-09-24T09:12:05.301Z"
}
],
"hasMore": false,
"nextCursor": null
}Create a subscription
/v2/webhookscost 1Creates a subscription and returns it with 201, along with the signing secret in the secret field. Save the secret right away: the API won't return it again.
Parameters
Idempotency-KeystringheaderoptionalA key that keeps a retried request from creating a second subscription — see Idempotency.
urlstringbodyrequiredA public
https://URL, up to 2000 characters — see Limits.eventsenum[]bodyrequiredA non-empty array of events:
message.received,message.sent,comment.received. Duplicates are removed.descriptionstring | nullbodyoptionalUp to 500 characters, or
null.enabledbooleanbodyoptionalfalse— the subscription exists but no events are sent. Defaults totrue.
- The URL is checked on creation. A non-
httpsURL, a private address or a host that doesn't resolve returns400 invalid_webhook_urlwithparam: "url"; the reason is inmessage. - Send an
Idempotency-Key: if the response gets lost, a retry with the same key returns the same subscription with the same secret instead of creating a second one. - After creating it, check the endpoint with a test event.
curl -X POST "https://api.pager.co.ua/v2/webhooks" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Idempotency-Key: a3f1c9e2-5b7d-4e8a-9c0b-1d2e3f4a5b6c" \
-H "Content-Type: application/json" \
-d '{"url":"https://crm.example.com/pager/webhook","events":["message.received","message.sent"],"description":"Синхронізація з CRM"}'{
"id": "8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b",
"url": "https://crm.example.com/pager/webhook",
"events": [
"message.received",
"message.sent"
],
"description": "Синхронізація з CRM",
"enabled": true,
"consecutiveFailures": 0,
"failingSince": null,
"disabledAt": null,
"disabledReason": null,
"createdAt": "2026-09-24T09:12:05.301Z",
"updatedAt": "2026-09-24T09:12:05.301Z",
"secret": "whsec_2Xk9vQ7mB4nR1tY8wZ3cL6pF0hJ5sD2gA9eU4iO7qWx"
}Retrieve a subscription
/v2/webhooks/{id}cost 1Returns the subscription and delivery stats for the last 30 days in stats: delivered — delivered, pending — queued or waiting for a retry, dead — not delivered after all attempts.
Parameters
idstringpathrequiredSubscription ID.
curl "https://api.pager.co.ua/v2/webhooks/8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"id": "8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b",
"url": "https://crm.example.com/pager/webhook",
"events": [
"message.received",
"message.sent"
],
"description": "Синхронізація з CRM",
"enabled": true,
"consecutiveFailures": 0,
"failingSince": null,
"disabledAt": null,
"disabledReason": null,
"createdAt": "2026-09-24T09:12:05.301Z",
"updatedAt": "2026-09-24T09:12:05.301Z",
"stats": {
"periodDays": 30,
"delivered": 1284,
"pending": 1,
"dead": 3
}
}Update a subscription
/v2/webhooks/{id}cost 1Changes only the fields you pass. A body with no fields returns 400.
Parameters
idstringpathrequiredSubscription ID.
urlstringbodyoptionalA public
https://URL, up to 2000 characters — see Limits.eventsenum[]bodyoptionalA non-empty array of events:
message.received,message.sent,comment.received. Duplicates are removed.descriptionstring | nullbodyoptionalUp to 500 characters, or
null.enabledbooleanbodyoptionalfalse— disable the subscription (disabledReason: "manual").true— enable it with a clean slate:consecutiveFailures,failingSince,disabledAtanddisabledReasonare reset.
- A new
urlis checked the same way as on creation. Changing the URL keeps the same signing secret. - Re-enabling after automatic disabling is exactly
enabled: true.
curl -X PATCH "https://api.pager.co.ua/v2/webhooks/8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b" \
-H "Authorization: Bearer $PAGER_API_KEY" \
-H "Content-Type: application/json" \
-d '{"events":["message.received"]}'{
"id": "8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b",
"url": "https://crm.example.com/pager/webhook",
"events": [
"message.received"
],
"description": "Синхронізація з CRM",
"enabled": true,
"consecutiveFailures": 0,
"failingSince": null,
"disabledAt": null,
"disabledReason": null,
"createdAt": "2026-09-24T09:12:05.301Z",
"updatedAt": "2026-09-24T12:05:17.640Z"
}Delete a subscription
/v2/webhooks/{id}cost 1Deletes the subscription together with its delivery log and responds with 204. Queued deliveries are not sent.
Parameters
idstringpathrequiredSubscription ID.
curl -X DELETE "https://api.pager.co.ua/v2/webhooks/8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b" \
-H "Authorization: Bearer $PAGER_API_KEY"Rotate the secret
/v2/webhooks/{id}/rotate-secretcost 1Creates a new signing secret and returns the subscription with it in the secret field.
Parameters
idstringpathrequiredSubscription ID.
- The old secret stops working immediately — there's no grace period. Deliveries already in the queue will be signed with the new secret.
curl -X POST "https://api.pager.co.ua/v2/webhooks/8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b/rotate-secret" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"id": "8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b",
"url": "https://crm.example.com/pager/webhook",
"events": [
"message.received",
"message.sent"
],
"description": "Синхронізація з CRM",
"enabled": true,
"consecutiveFailures": 0,
"failingSince": null,
"disabledAt": null,
"disabledReason": null,
"createdAt": "2026-09-24T09:12:05.301Z",
"updatedAt": "2026-09-24T12:10:48.093Z",
"secret": "whsec_Hn4tE8rW1qZ6xC3vB9mK2pL7sD5fG0jA4yU8iO1eRtN"
}Send a test event
/v2/webhooks/{id}/testcost 1Synchronously sends a webhook.test event to the subscription's URL and returns the delivery record with the result: delivered or dead, your server's response code and body, and the duration.
Parameters
idstringpathrequiredSubscription ID.
- A test event isn't retried on failure and doesn't affect the subscription's failure streak. It does appear in the log.
- Works for a disabled subscription too: handy for checking a fixed endpoint before enabling it.
- In a test event
data.messageis a string, not a message object.
curl -X POST "https://api.pager.co.ua/v2/webhooks/8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b/test" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"id": "dlv_0f1e2d3c-4b5a-4968-8776-5a4b3c2d1e0f",
"eventId": "evt_a0b1c2d3e4f5460718293a4b5c6d7e8f",
"eventType": "webhook.test",
"status": "delivered",
"attempt": 1,
"nextAttemptAt": null,
"responseStatus": 200,
"responseBody": "ok",
"durationMs": 184,
"createdAt": "2026-09-24T09:13:40.210Z",
"deliveredAt": "2026-09-24T09:13:40.402Z"
}Delivery log
/v2/webhooks/{id}/deliveriescost 2The subscription's deliveries, paginated, newest first. One record is one event: attempt shows how many attempts have been made, and responseStatus, responseBody and durationMs show the result of the latest one.
Parameters
idstringpathrequiredSubscription ID.
statusenumqueryoptionalpending,failed,deliveredordead.eventTypestringqueryoptionalOnly deliveries of this event type, e.g.
webhook.test.limitintegerqueryoptionalPage size,
1to100. Defaults to50.cursorstringqueryoptionalnextCursorfrom the previous page.
status:pending— waiting to be sent,failed— the last attempt failed, the next one is atnextAttemptAt,delivered— delivered atdeliveredAt,dead— all attempts used up.responseStatusisnullif there was no response (timeout, connection error); the reason is then inresponseBody.- The log doesn't return the event body itself. Records are kept for 30 days.
curl "https://api.pager.co.ua/v2/webhooks/8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b/deliveries?limit=2" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"data": [
{
"id": "dlv_4b7e1f0a-9c2d-4e8b-a6f3-5d1c0b9e8a72",
"eventId": "evt_6c1f0e9d8b7a45c3a2e1f0d9c8b7a6e5",
"eventType": "message.received",
"status": "delivered",
"attempt": 1,
"nextAttemptAt": null,
"responseStatus": 200,
"responseBody": "ok",
"durationMs": 184,
"createdAt": "2026-09-24T08:31:40.582Z",
"deliveredAt": "2026-09-24T08:31:40.771Z"
},
{
"id": "dlv_9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
"eventId": "evt_1d2c3b4a5f6e47d8a9b0c1d2e3f4a5b6",
"eventType": "message.received",
"status": "failed",
"attempt": 3,
"nextAttemptAt": "2026-09-24T08:36:12.004Z",
"responseStatus": 503,
"responseBody": "Service Unavailable",
"durationMs": 91,
"createdAt": "2026-09-24T08:29:03.117Z",
"deliveredAt": null
}
],
"hasMore": true,
"nextCursor": "WyIyMDI2LTA5LTI0VDA4OjMxOjQwLjU4MloiLCI0YjdlMWYwYS05YzJkLTRlOGItYTZmMy01ZDFjMGI5ZThhNzIiXQ"
}Retry a delivery
/v2/webhooks/{id}/deliveries/{deliveryId}/retrycost 1Queues the delivery to be sent immediately and responds with 202 and the record in pending status. The result shows up in the log within a few seconds.
Parameters
idstringpathrequiredSubscription ID.
deliveryIdstringpathrequiredDelivery
idfrom the log,dlv_….
- Works for any status, including
dead. Fordeadit's a single extra attempt: if it fails, the delivery becomesdeadagain. - Enable a disabled subscription first: the retry would never be sent, so the API returns
409 webhook_disabled. - The event goes out with the same
id, so if your server has already processed it, it will drop it as a duplicate.
curl -X POST "https://api.pager.co.ua/v2/webhooks/8f3a2c1e-7b6d-4e5f-9a0b-1c2d3e4f5a6b/deliveries/dlv_9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d/retry" \
-H "Authorization: Bearer $PAGER_API_KEY"{
"id": "dlv_9a8b7c6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
"eventId": "evt_1d2c3b4a5f6e47d8a9b0c1d2e3f4a5b6",
"eventType": "message.received",
"status": "pending",
"attempt": 3,
"nextAttemptAt": "2026-09-24T08:33:20.518Z",
"responseStatus": 503,
"responseBody": "Service Unavailable",
"durationMs": 91,
"createdAt": "2026-09-24T08:29:03.117Z",
"deliveredAt": null
}
Resources
Comments
Clients' comments under Instagram posts and the replies to them. Each comment belongs to the conversation with its author, so comments are read and written within a conversation. The tree has two levels: a thread is a client's comment under a post, and
repliesare the replies in it, oldest first. Threads are returned newest first.422 channel_unsupported.Thread object
idstringComment ID in Pager.
conversationIdstringThe conversation with the comment's author.
parentIdstring | nullThe thread root for a reply, or
nullfor the root itself.directionenumincoming— a client's comment,outgoing— a reply on behalf of the page.textstringComment text.
usernamestringWho wrote it: the client's username, or the channel name for page replies.
authorIdstring | nullThe manager who replied.
nullfor client comments and for replies sent via the API.mediaobject | nullThe post the comment is under:
idandurl.nullif unknown.hasPrivateReplybooleanWhether a private reply has already been sent to the author's DMs. Meta allows this only once per comment.
createdAtISO 8601When the comment was written.
updatedAtISO 8601When the comment was last changed.
repliesobject[]Threads only: replies to the comment — same fields, oldest first.
Conversation comments
/v2/conversations/{id}/commentscost 2Returns all comment threads of the conversation with their replies, without pagination.
Parameters
idstringpathrequiredConversation ID.
Reply to a comment
/v2/conversations/{id}/commentscost 1outbound 1Replies to a comment publicly under the post or privately in the author's DMs. Responds with
201and avisibilityfield: forpublic, the new reply is incomment; forprivate, the sent message is inmessageand the updated comment is incomment(hasPrivateReply: true).Parameters
idstringpathrequiredConversation ID.
Idempotency-KeystringheaderoptionalA key that keeps a retried request from sending the reply twice — see Idempotency.
replyToCommentIdstringbodyrequiredidof a comment in this conversation that you're replying to.visibilityenumbodyrequiredpublic— a public reply under the post on behalf of the page,private— a direct message to the comment's author.textstringbodyrequiredReply text, 1 to 2000 characters.
replyToCommentIdis a reply, Pager replies to its root.400 not_a_client_comment) and only once: a second one returns409 private_reply_exists. It shows up in the conversation as a regular outgoing message and triggers the `message.sent` webhook.402 message_limit_reached.422 channel_rejectedwith the reason. A failed private reply is saved as an undelivered message, and itsidis in the error text.Idempotency-Key. Besides a cost of1in thegeneralbucket, it takes1from theoutboundbucket.Delete a comment
/v2/comments/{id}cost 1outbound 1Deletes the comment on Instagram and in Pager together with all replies to it, and responds with
204.Parameters
idstringpathrequiredConversation ID.
422 channel_rejected, and nothing is deleted in Pager.1from theoutboundbucket, like a reply.