Typing Indicators API
Send and receive iMessage typing indicators via the Sendblue API
Typing indicators (the ”…” bubble in iMessage) let recipients know when someone is composing a message. The Sendblue API supports both sending typing indicators to your contacts and receiving real-time notifications when your contacts are typing.
Sending Typing Indicators
Section titled “Sending Typing Indicators”Display a typing indicator to a recipient, showing them that a message is being composed.
POST https://api.sendblue.com/api/send-typing-indicatorRequest Body
Section titled “Request Body”| Parameter | Type | Required | Description |
|---|---|---|---|
number | string | Yes | The recipient’s phone number (E.164 format) |
from_number | string | No | Your Sendblue line number to send from (E.164 format) |
state | string | No | "start" (default) or "stop". Use "stop" to end an active typing indicator before its max_duration_ms expires (for example, when transferring to a human agent or canceling an AI reply). |
max_duration_ms | integer | No | How long (in milliseconds) the typing indicator should remain visible before automatically stopping. Defaults to 60000 (60 seconds). Must be between 1 and 300000 (5 minutes). |
Example Request
Section titled “Example Request”curl -X POST 'https://api.sendblue.com/api/send-typing-indicator' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET' \ -H 'Content-Type: application/json' \ -d '{ "number": "+14155551234", "from_number": "+19175551234" }'Showing a typing indicator for a specific duration
Section titled “Showing a typing indicator for a specific duration”Useful for AI agents whose response time is variable — fire a longer indicator while the model thinks, then call state: "stop" as soon as the reply is ready.
curl -X POST 'https://api.sendblue.com/api/send-typing-indicator' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET' \ -H 'Content-Type: application/json' \ -d '{ "number": "+14155551234", "state": "start", "max_duration_ms": 120000 }'Stopping a typing indicator early
Section titled “Stopping a typing indicator early”curl -X POST 'https://api.sendblue.com/api/send-typing-indicator' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET' \ -H 'Content-Type: application/json' \ -d '{ "number": "+14155551234", "state": "stop" }'Calling state: "stop" for a recipient who currently has no active indicator is a safe no-op.
Node.js Example
Section titled “Node.js Example”const axios = require('axios');
await axios.post( 'https://api.sendblue.com/api/send-typing-indicator', { number: '+14155551234', from_number: '+19175551234' }, { headers: { 'sb-api-key-id': 'YOUR_API_KEY', 'sb-api-secret-key': 'YOUR_API_SECRET', 'Content-Type': 'application/json' } });Python Example
Section titled “Python Example”import requests
response = requests.post( 'https://api.sendblue.com/api/send-typing-indicator', json={ 'number': '+14155551234', 'from_number': '+19175551234' }, headers={ 'sb-api-key-id': 'YOUR_API_KEY', 'sb-api-secret-key': 'YOUR_API_SECRET' })Success Response (200)
Section titled “Success Response (200)”{ "status": "QUEUED", "status_code": 200, "error_message": null, "number": "+14155551234"}Error Responses
Section titled “Error Responses”Missing Number (400)
Section titled “Missing Number (400)”{ "status": "ERROR", "message": "You must specify a `number` in the request body."}Invalid state value (400)
Section titled “Invalid state value (400)”{ "status": "ERROR", "message": "`state` must be either \"start\" or \"stop\"."}Invalid max_duration_ms value (400)
Section titled “Invalid max_duration_ms value (400)”{ "status": "ERROR", "message": "`max_duration_ms` must be an integer between 1 and 300000."}Worker firmware does not yet support typing-v2 (503)
Section titled “Worker firmware does not yet support typing-v2 (503)”Returned when the line’s underlying worker is on an older firmware that doesn’t honor state: "stop" or an explicit max_duration_ms. The fleet rolls out continuously — retry shortly. Legacy start requests with no duration are unaffected and always succeed.
{ "status": "ERROR", "status_code": 503, "error_message": "Worker firmware iowa-1.9.80 does not yet support typing-v2 state=\"stop\". Fleet update is rolling out; retry shortly.", "number": "+14155551234"}No Route Mapping (400)
Section titled “No Route Mapping (400)”{ "status": "ERROR", "status_code": 400, "error_message": "No route mapping found for number, have you messaged them before?", "number": "+14155551234"}Receiving Typing Indicators
Section titled “Receiving Typing Indicators”Get notified in real-time when your contacts start or stop typing via webhooks.
Configuring the Webhook
Section titled “Configuring the Webhook”Register a typing_indicator webhook to receive typing events:
curl -X POST 'https://api.sendblue.com/api/account/webhooks' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET' \ -H 'Content-Type: application/json' \ -d '{ "webhooks": ["https://your-server.com/webhooks/typing"], "type": "typing_indicator" }'Webhook Payload
Section titled “Webhook Payload”When a contact starts or stops typing, Sendblue will POST to your configured webhook:
{ "number": "+14155551234", "is_typing": true, "from_number": "+19175551234", "timestamp": "2025-01-30T12:34:56.789Z"}Payload Fields
Section titled “Payload Fields”| Field | Type | Description |
|---|---|---|
number | string | The phone number of the person typing (E.164 format) |
is_typing | boolean | true when typing starts, false when typing stops |
from_number | string | Your Sendblue line number that received the indicator |
timestamp | string | ISO 8601 timestamp of when the event was received |
Node.js Webhook Handler Example
Section titled “Node.js Webhook Handler Example”const express = require('express');const app = express();
app.use(express.json());
app.post('/webhooks/typing', (req, res) => { const { number, is_typing, from_number, timestamp } = req.body;
if (is_typing) { console.log(`${number} started typing...`); // Show typing indicator in your UI } else { console.log(`${number} stopped typing`); // Hide typing indicator in your UI }
res.status(200).send('OK');});
app.listen(3000);Python Webhook Handler Example
Section titled “Python Webhook Handler Example”from flask import Flask, request
app = Flask(__name__)
@app.route('/webhooks/typing', methods=['POST'])def handle_typing(): data = request.json number = data.get('number') is_typing = data.get('is_typing')
if is_typing: print(f'{number} started typing...') # Show typing indicator in your UI else: print(f'{number} stopped typing') # Hide typing indicator in your UI
return 'OK', 200-
iMessage Only: Typing indicators are only supported for iMessage conversations, not SMS or RCS.
-
Prior Conversation Required: To send a typing indicator, you must have an existing conversation with the recipient. The API uses the established route mapping to deliver the indicator.
-
Real-time Events: Typing indicator webhooks are delivered in real-time as events occur on the device.
-
Webhook Security: You can configure a secret for your typing indicator webhook to verify that requests are coming from Sendblue. See the Webhooks documentation for details.
-
Best Practices:
- Send a typing indicator before composing a response to create a more natural conversation flow
- Use the
is_typingboolean to toggle UI state in your application - Always respond with a 200-level status code to acknowledge receipt of webhook events
Auto Typing Indicator
Section titled “Auto Typing Indicator”Plan availability: Auto typing indicator is available on the Free API plan and the AI Agent plan. You can enable it from the account settings page in your dashboard, or via the API endpoint below.
When auto typing indicator is enabled, Sendblue automatically sends a typing indicator on every inbound 1:1 iMessage your line receives. Useful for AI-agent and async-workflow setups: the sender sees the ”…” bubble immediately while your backend composes a real response.
What auto-fires
Section titled “What auto-fires”The auto-trigger only runs when all of these are true:
- The inbound is an iMessage (typing indicators don’t apply to SMS or RCS)
- It’s a 1:1 conversation, not a group chat
- It’s the first part of the inbound (multi-attachment messages don’t fire twice)
If any of those don’t hold, no auto typing indicator is sent and the inbound flows through normally.
Enable / Disable
Section titled “Enable / Disable”POST https://api.sendblue.com/accounts/settings/auto-typing-indicatorRequest Body
Section titled “Request Body”| Parameter | Type | Required | Description |
|---|---|---|---|
auto_typing_indicator | boolean | Yes | true to enable, false to disable |
Example Request
Section titled “Example Request”curl -X POST 'https://api.sendblue.com/accounts/settings/auto-typing-indicator' \ -H 'sb-api-key-id: YOUR_API_KEY' \ -H 'sb-api-secret-key: YOUR_API_SECRET' \ -H 'Content-Type: application/json' \ -d '{ "auto_typing_indicator": true }'Node.js Example
Section titled “Node.js Example”const axios = require('axios');
await axios.post( 'https://api.sendblue.com/accounts/settings/auto-typing-indicator', { auto_typing_indicator: true }, { headers: { 'sb-api-key-id': 'YOUR_API_KEY', 'sb-api-secret-key': 'YOUR_API_SECRET', 'Content-Type': 'application/json' } });Python Example
Section titled “Python Example”import requests
response = requests.post( 'https://api.sendblue.com/accounts/settings/auto-typing-indicator', json={'auto_typing_indicator': True}, headers={ 'sb-api-key-id': 'YOUR_API_KEY', 'sb-api-secret-key': 'YOUR_API_SECRET' })Success Response (200)
Section titled “Success Response (200)”{ "status": "OK", "auto_typing_indicator": true}Error Responses
Section titled “Error Responses”Missing or non-boolean value (400)
Section titled “Missing or non-boolean value (400)”{ "status": "ERROR", "message": "auto_typing_indicator must be a boolean"}- Setting takes effect on the next inbound. Cached account data invalidates immediately after the toggle write.
- Auto and manual coexist. Toggling auto on doesn’t disable the manual
POST /api/send-typing-indicatorendpoint — you can still send explicit indicators when you want (for example, before sending a follow-up message in the same conversation). - Same delivery semantics as the manual endpoint: iMessage-only, requires an existing conversation with the recipient.