Read Receipts & Typing Indicators
Two lightweight sender actions let your integration behave like a person on the other end of the chat:
- Read receipt: show the contact that their message was read (for example blue ticks on WhatsApp, "Seen" on Messenger and Instagram).
- Typing indicator: show "typing…" while your bot or backend prepares a reply.
Sender actions are not messages:
- They are not billed and do not count towards your message pool.
- No message is created in the conversation.
- No webhook is sent.
- Your inbox's own unread state is not changed. Marking a message as read for the contact does not mark the conversation as read for your team.
Required Scopes
| Scope | Purpose |
|---|---|
messages:create | Send read receipts and typing indicators |
channels:read | Check which channels support them |
Channel Support
| Channel | Read receipt | Typing indicator | Can be cleared (off) | Typing lasts |
|---|---|---|---|---|
| Yes | Yes (also marks as read) | No | Up to 25 s or until you reply | |
| Messenger | Yes | Yes | Yes | About 20 s |
| Yes | Yes | Yes | About 20 s | |
| Telegram | No | Yes | No | About 5 s |
| RCS | Yes | Yes | No | About 20 s or until you reply |
| Live Chat | No | Yes | Yes | Until cleared or until you reply |
| SMS | No | No | No | — |
| No | No | No | — | |
| Browser Push | No | No | No | — |
| Viber | No | No | No | — |
Messenger and Instagram only accept sender actions inside the standard 24-hour messaging window.
Always check support at runtime instead of hard-coding this table:
GET /api/v1/channels/sender-actions
curl "https://api.sendseven.com/api/v1/channels/sender-actions" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"
{
"items": [
{
"channel_type": "whatsapp",
"read": true,
"typing": true,
"typing_off": false,
"typing_ttl_seconds": 25,
"typing_requires_message": true
},
{
"channel_type": "telegram",
"read": false,
"typing": true,
"typing_off": false,
"typing_ttl_seconds": 5,
"typing_requires_message": false
}
],
"pagination": {
"total": 10,
"page": 1,
"page_size": 10,
"total_pages": 1,
"has_next": false,
"has_prev": false
}
}
Every channel type is listed; unsupported ones report false. The same object (without channel_type) is returned as sender_actions on GET /channels/{channel_id}/capabilities.
| Field | Meaning |
|---|---|
read | Read receipts are supported |
typing | Typing indicators are supported |
typing_off | The indicator can be cleared with state: "off". When false, off is accepted but does nothing; the indicator ends on its own |
typing_ttl_seconds | How long one typing call stays visible. null = until cleared or until you reply |
typing_requires_message | The platform ties typing to a received message. SendSeven uses the latest inbound message if you don't pass message_id |
Mark a Message as Read
POST /api/v1/messages/{message_id}/read
No request body is needed. Only inbound messages (sent by the contact) can be marked as read.
curl -X POST "https://api.sendseven.com/api/v1/messages/f47ac10b-58cc-4372-a567-0e02b2c3d479/read" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00"
{
"success": true,
"action": "read",
"state": null,
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"conversation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"channel_type": "whatsapp",
"expires_in_seconds": null,
"implied_read": false,
"deduplicated": false
}
Show or Clear a Typing Indicator
POST /api/v1/conversations/{conversation_id}/typing
Request Body (optional)
| Field | Type | Required | Description |
|---|---|---|---|
state | string | No | on (default) or off |
message_id | string | No | An inbound message of this conversation to attach the indicator to. Only used where typing_requires_message is true; defaults to the latest inbound message |
An empty body is the same as {"state": "on"}.
curl -X POST "https://api.sendseven.com/api/v1/conversations/7c9e6679-7425-40de-944b-e07fc1f90ae7/typing" \
-H "Authorization: Bearer s7_api_a1b2c3d4e5f6789012345678abcdef00" \
-H "Content-Type: application/json" \
-d '{"state": "on"}'
{
"success": true,
"action": "typing",
"state": "on",
"message_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"conversation_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
"channel_type": "whatsapp",
"expires_in_seconds": 25,
"implied_read": true,
"deduplicated": false
}
expires_in_seconds: when the platform will hide the indicator on its own. Call again before then to keep it visible during a long operation (for example every 4 seconds on Telegram, every 20 seconds on WhatsApp).implied_read:truewhen the indicator also marked the message as read. On WhatsApp a typing indicator always marks the contact's message as read — don't use it if you don't want the contact to see read ticks.- Sending your reply ends the indicator on every channel; you don't need to send
offfirst.
Repeated Calls & Limits
You don't need to track what you already sent — SendSeven absorbs repeats:
- Read receipts are sent once per message. Marking the same message as read again returns
200withdeduplicated: trueand nothing is sent to the channel. - Typing
onis deduplicated. Anotheronfor the same conversation within half of the channel's typing duration (typing_ttl_seconds) returns200withdeduplicated: true; the indicator is still showing. Refresh it at roughly half the duration to keep it visible continuously. - Typing
offis never deduplicated or limited. - Rate limit: about 30 read/typing calls per minute per conversation reach the channel. Above that you get
429 sender_action_rate_limitedwith aRetry-Afterheader (seconds).
deduplicated is false whenever the action was actually sent.
Errors
Errors use a structured detail object:
{
"detail": {
"code": "sender_action_not_supported",
"message": "Read receipts are not supported on this channel.",
"channel_type": "telegram",
"action": "read"
}
}
| HTTP | code | When |
|---|---|---|
| 404 | message_not_found | The message doesn't exist, isn't visible to you, or (typing) isn't in this conversation |
| 404 | conversation_not_found | The conversation doesn't exist or isn't visible to you |
| 422 | sender_action_not_supported | The channel doesn't support this action (see the table above) |
| 422 | message_not_inbound | You tried to mark one of your own outbound messages as read |
| 422 | no_inbound_message | Typing on a channel that needs a received message, but the conversation has none |
| 422 | channel_unavailable | The channel is disconnected or can't be used right now |
| 429 | sender_action_rate_limited | More than about 30 calls per minute for this conversation. Wait for Retry-After seconds |
| 502 | sender_action_failed | The messaging platform rejected the request (for example outside the 24-hour window on Messenger) |
For 502 sender_action_failed, message is a generic, safe description. When the cause is known it may include a short hint (for example that the messaging window has closed). The platform's raw error is never passed through, so don't parse message — branch on code.
Sender actions are best-effort UX signals: treat a failure as non-fatal and send your reply anyway.