Notify Africa

Messaging

Send freeform text and approved WhatsApp template messages.

The Developer API exposes two send operations. Both return one result per recipient and process accepted results asynchronously.

Prerequisites

  • Portal reports the WABA connection as ready.
  • API key has WABA access.
  • Connected account has a selected phone number.
  • Template sends use an approved template.
  • Text sends target a recipient with an open 24-hour customer-service window.

The backend resolves the owned WABA and selected phone number. Do not send business, WABA, or phone-number IDs.

Authentication

Authorization: Bearer ntfy_your_api_key
Content-Type: application/json

Send Text

POST /v1/waba-api/messages/text

Use this operation only after the customer has sent a qualifying inbound message during the preceding 24 hours.

curl --request POST \
  "$NOTIFY_API_BASE_URL/v1/waba-api/messages/text" \
  --header "Authorization: Bearer $NOTIFY_WABA_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "to": ["255700000001", "255700000002"],
    "text": "Your support request has been updated.",
    "idempotency_key": "support-update-1001"
  }'

Request Body

FieldRequiredValidation
toyesString or string array; 1-500 entries after trimming/deduplication
textyesString, 1-4096 characters
idempotency_keynoString, maximum 128 characters

Success

HTTP 200:

{
  "status": 200,
  "message": "Success",
  "timestamp": "2026-09-09T10:30:00.000Z",
  "path": "/v1/waba-api/messages/text",
  "data": {
    "results": [
      {
        "to": "255700000001",
        "success": true,
        "messageId": "d9b54c80-8a63-4f8b-83c8-71f2439f5429",
        "status": "processing",
        "error": null
      },
      {
        "to": "255700000002",
        "success": false,
        "error": "Freeform text is not currently permitted for this recipient. Use an approved template send instead."
      }
    ]
  }
}

HTTP 200 can contain both successful and failed recipient results. Inspect every entry.

Errors

  • Invalid top-level DTO or more than 500 recipients: request-level 400.
  • Closed, unknown, or expired service window: per-recipient success: false.
  • Missing selected phone number: per-recipient failure.
  • Invalid API key: request-level 401.
  • Queue/dependency failure: request-level error or per-recipient failure, depending on where processing stops.

Related: subscribe to message.received to know when a customer opens a conversation, then process message.status.updated after sending.

Next: store each messageId and wait for status events.

Send Template Message

POST /v1/waba-api/messages/template

Template messages can start a conversation and can be sent outside the customer-service window.

curl --request POST \
  "$NOTIFY_API_BASE_URL/v1/waba-api/messages/template" \
  --header "Authorization: Bearer $NOTIFY_WABA_API_KEY" \
  --header "Content-Type: application/json" \
  --data '{
    "to": "255700000001",
    "template_name": "order_update",
    "language": "en",
    "template_parameters": {
      "body": {
        "1": "Jane",
        "2": "ORD-1001"
      }
    },
    "idempotency_key": "order-1001-update"
  }'

Request Body

FieldRequiredValidation
toyesString or string array; 1-500 entries after trimming/deduplication
template_nameyesNon-empty approved external WhatsApp template name
template_parametersnoObject; flat body bindings or grouped components
languagenoString; defaults to en
idempotency_keynoString, maximum 128 characters

Success

HTTP 200 with the same envelope and per-recipient result shape as text sends:

{
  "status": 200,
  "message": "Success",
  "timestamp": "2026-09-09T10:31:00.000Z",
  "path": "/v1/waba-api/messages/template",
  "data": {
    "results": [
      {
        "to": "255700000001",
        "success": true,
        "messageId": "fe245daf-b447-4de7-9af4-35a2ab6cdd71",
        "status": "processing",
        "error": null
      }
    ]
  }
}

Provider approval and parameter compatibility are checked during asynchronous worker preflight. A queued result can later become failed.

Errors

  • Invalid top-level DTO or recipient list: request-level 400.
  • Missing selected phone number: per-recipient failure.
  • Missing, unapproved, mismatched-language, or incompatible template: asynchronous failed status can follow initial queue acceptance.
  • Invalid provider parameters: asynchronous provider failure.
  • Invalid API key: request-level 401.

Related: use the template status endpoint before sending and subscribe to template.status.updated and message.status.updated.

Next: correlate status callbacks using messageId or wamid.

Recipient Processing

The to field accepts a single string or an array.

The API:

  1. trims each string.
  2. removes empty and non-string values.
  3. removes exact duplicate strings.
  4. preserves the remaining order.
  5. rejects an empty result.
  6. rejects more than 500 recipients.

Phone numbers are not normalized or validated as E.164 by this facade. Use one consistent international format without spaces or punctuation. 255700000001 and +255700000001 are different recipient strings.

Idempotency

With a client key, the API derives one key per recipient:

<idempotency_key>:<recipient>

Use one stable value for retries of the same logical send. Use a new value for a new send.

When omitted, the API creates a random key per recipient. Retries without a stable client key are not deduplicated as the same logical request.

Template Parameters

Flat body values

{
  "template_parameters": {
    "1": "Jane",
    "2": "ORD-1001"
  }
}

Grouped values

{
  "template_parameters": {
    "header": {
      "1": "Acme"
    },
    "body": {
      "1": "Jane",
      "2": "ORD-1001"
    },
    "buttons": [
      {
        "sub_type": "quick_reply",
        "parameters": [
          {
            "type": "payload",
            "payload": "order_1001"
          }
        ]
      }
    ]
  }
}
  • Flat objects become body parameters.
  • Body/header scalar values become text parameters.
  • Numeric keys sort numerically; remaining keys sort alphabetically.
  • Button entries are forwarded as button components.
  • The Developer API mapper does not convert media URLs into media-header parameters.

Match the approved template's exact component and variable order.

Result Fields

FieldPresenceMeaning
toalwaysNormalized recipient string used for this result
successalwaysWhether this recipient entered the accepted send path
messageIdsuccessful result when persistedNotify Africa message identifier
wamidoptionalMeta message identifier, often unavailable in the initial response
statusoptionalCurrent local processing state
erroroptionalRecipient-specific rejection or processing reason

Message Lifecycle

processing/queued
sending
accepted
sent
delivered
read

Any processing stage can become failed. Provider values that are not mapped can appear as unknown.

There is no API-key message read/status operation. Use signed webhook events for asynchronous state changes and the Portal for operational inspection.

On this page