BotPulsar

Home/ Developers/Simple send API

Simple send API

Send WhatsApp from your app with one call

You already run a BotPulsar workspace with a connected number. Give your ERP, billing system or CRM a key, and it can send — no contacts or conversations to set up first.

POST /api/v1/partner-api/workspace/messages/ with to, a template (or text) and an idempotency_key sends a WhatsApp message from your workspace. The key identifies the workspace, so X-Tenant-ID is optional, and the contact and conversation are created for you. Limit: 20,000 requests per hour per key.

Set up

Four steps, nobody touches a server

  1. Make sure the workspace is ready

    A connected WhatsApp number, a payment method on the WhatsApp Business Account in Meta, and a plan that includes API access (Growth and above). Install the templates you will send in Templates and wait for Meta to approve them.

  2. Create a key

    Operations → Developer → Create API key (owner or admin). Grant messages:read contacts:write conversations:write messages:write to send and track status, plus templates:read to list templates and webhooks:write to connect a webhook. Copy the secret now — it is shown once.

  3. Paste it into your app

    That is all the app needs. Test it with GET /api/v1/partner-api/workspace/ — it returns your workspace and connected numbers.

  4. Optionally connect the webhook

    Call POST …/workspace/webhook/ with your app's URL to receive delivery status and replies. Verification works exactly as on the Webhooks page.

Auth

Headers

Headers
Authorization: ApiKey smk_your_prefix_your_secret
Content-Type: application/json

Same smk_ key as everywhere else; the scope for each call is listed below. If you do send X-Tenant-ID it must match the key's workspace.

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

FieldTypeRequiredDescription
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

FieldTypeRequiredDescription
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

FieldTypeRequiredDescription
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

FieldTypeRequiredDescription
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.
Good to know

Behaviour worth planning for

Always send an idempotency_key

Use a stable id from your system (invoice number, receipt id). A retry with the same key returns the original message with 200 instead of sending twice.

First message needs a template

To a customer who has not messaged you in the last 24 hours, WhatsApp only allows an approved template. Plain text then fails with 409 whatsapp_template_required.

Delivery is asynchronous

The response says queued. Use the webhook, or poll GET …/messages/{id}/, to see delivered, read or failed.

Opted-out contacts are skipped

A customer who replied STOP is suppressed; sending returns 409 contact_suppressed and nothing is queued.

Copy a working example Postman collection

Start on WhatsApp today — from ₹300/month

Unlimited team members on every plan. 14-day trial, no setup fee, no annual lock-in.