Endpoints
Simple send API reference
For a business that already runs its own BotPulsar workspace and wants another app — an ERP, a billing system, a CRM — to send from it. The app only needs the key: it identifies the workspace, so X-Tenant-ID is optional (if you send it, it must match).
Check the connection
GET /api/v1/partner-api/workspace/
Scope
messages:read
Returns the workspace and its connected WhatsApp numbers — the quickest way to test a key.
Request
curl -X GET https://api.botpulsar.com/api/v1/partner-api/workspace/ \
-H "Authorization: ApiKey smk_your_prefix_your_secret"
Response
{
"workspace_id": "3f9c7e2a-1b4d-4e8a-9c3f-2a1b4d4e8a9c",
"name": "Springfield School",
"channels": [
{
"display_phone_number": "+91 98123 45678",
"quality_rating": "GREEN",
"can_send": true,
"...": "..."
}
]
}
Send a template message
POST /api/v1/partner-api/workspace/messages/
Scope
contacts:write + conversations:write + messages:write
Finds or creates the contact and conversation, then sends. 201 for a new message, 200 when idempotency_key was already used.
Request body
| Field | Type | Required | Description |
to |
string |
Yes |
Recipient, E.164. Max 32 characters. |
template |
object |
Yes |
{name, language, params[]} — an approved template and its variable values in order. |
idempotency_key |
string |
Yes |
Your own unique id for this send (1–128 of letters, numbers, ., _, :, -). |
phone_number_id |
uuid |
No |
Send from a specific number. |
Request
curl -X POST https://api.botpulsar.com/api/v1/partner-api/workspace/messages/ \
-H "Authorization: ApiKey smk_your_prefix_your_secret" \
-H "Content-Type: application/json" \
-d '{
"to": "+919812345678",
"template": {
"name": "fee_due",
"language": "en",
"params": [
"Ada",
"₹4,500",
"15 Oct"
]
},
"idempotency_key": "fee-due-STU1042-2026-10"
}'
Response
{
"id": "6d1c0b0e-52a4-4b7f-9a55-0a43d6a7c0de",
"direction": "outbound",
"content_type": "template",
"delivery_status": "queued",
"client_message_id": "…",
"error": null,
"created_at": "2026-10-11T08:31:02Z"
}
- Send
text instead of template for free-form text inside the 24-hour window. - Errors:
template_not_found, template_not_approved, no_connected_number, invalid_phone, whatsapp_template_required, contact_suppressed, billing_limit_reached.
Send a text message
POST /api/v1/partner-api/workspace/messages/
Scope
contacts:write + conversations:write + messages:write
Free-form text, valid only while the customer's 24-hour window is open.
Request body
| Field | Type | Required | Description |
to |
string |
Yes |
Recipient, E.164. |
text |
string |
Yes |
Up to 4,096 characters. |
idempotency_key |
string |
Yes |
Your unique id for this send. |
Request
curl -X POST https://api.botpulsar.com/api/v1/partner-api/workspace/messages/ \
-H "Authorization: ApiKey smk_your_prefix_your_secret" \
-H "Content-Type: application/json" \
-d '{
"to": "+919812345678",
"text": "Thanks — we received your payment.",
"idempotency_key": "receipt-8841"
}'
Response
{
"id": "…",
"direction": "outbound",
"content_type": "text",
"delivery_status": "queued",
"...": "..."
}
Get a message's status
GET /api/v1/partner-api/workspace/messages/{message_id}/
Scope
messages:read
delivery_status moves queued → submitted → sent → delivered → read, or failed with an error. For an inbound message it also returns from and text — message.received webhooks carry ids only, so this is how you read the reply.
Request
curl -X GET https://api.botpulsar.com/api/v1/partner-api/workspace/messages/YOUR_MESSAGE_ID/ \
-H "Authorization: ApiKey smk_your_prefix_your_secret"
Response
{
"id": "…",
"direction": "inbound",
"delivery_status": "delivered",
"from": "+919812345678",
"text": "Yes, will pay today",
"...": "..."
}
List templates
GET /api/v1/partner-api/workspace/templates/
Scope
templates:read
Every template on the workspace's WhatsApp account with its status.
Request
curl -X GET https://api.botpulsar.com/api/v1/partner-api/workspace/templates/ \
-H "Authorization: ApiKey smk_your_prefix_your_secret"
Response
{
"results": [
{
"name": "fee_due",
"language": "en",
"status": "approved",
"...": "..."
}
]
}
Install a template pack
POST /api/v1/partner-api/workspace/templates/deploy-pack/
Scope
templates:read + templates:write
Creates a ready-made set of templates and submits them to Meta. Safe to re-run — it resubmits any left in draft by a failed submission.
Request body
| Field | Type | Required | Description |
pack |
string |
Yes |
Pack name, e.g. edugradup_school. |
submit |
boolean |
No |
Submit to Meta immediately (default true). |
waba_id |
uuid |
No |
Target a specific WhatsApp account. |
Request
curl -X POST https://api.botpulsar.com/api/v1/partner-api/workspace/templates/deploy-pack/ \
-H "Authorization: ApiKey smk_your_prefix_your_secret" \
-H "Content-Type: application/json" \
-d '{
"pack": "edugradup_school"
}'
Response
{
"pack": "edugradup_school",
"...": "..."
}
Connect your app's webhook
POST /api/v1/partner-api/workspace/webhook/
Scope
webhooks:write
Creates (or updates) the workspace's "Connected app" subscription and returns its signing secret. Calling it again rotates the secret.
Request body
| Field | Type | Required | Description |
url |
url |
Yes |
Public HTTPS endpoint. |
name |
string |
No |
Label, max 160 characters. |
Request
curl -X POST https://api.botpulsar.com/api/v1/partner-api/workspace/webhook/ \
-H "Authorization: ApiKey smk_your_prefix_your_secret" \
-H "Content-Type: application/json" \
-d '{
"url": "https://erp.example.com/hooks/botpulsar"
}'
Response
{
"webhook_secret": "whsec_…",
"workspace_id": "3f9c7e2a-1b4d-4e8a-9c3f-2a1b4d4e8a9c"
}
- Events delivered:
partner.channel.updated, partner.template.status_changed, message.delivery_status.changed, message.outbound.failed, message.received.