API Reference

v1 preview

The ZNotice v1 API is a simple JSON REST interface. GET /status, GET /messages, GET /usage, POST /send (immediate or scheduled), POST /bulk and the scheduled-queue / batch endpoints are live.

Documentation downloads (PDF)

Download the client-facing ZNotice documentation set as individual PDFs or one combined document.

Credits & billing

Messaging is pay-as-you-go at $0.01 per message ($10 = 1,000 credits). Buy credits from Dashboard → Billing.

  1. Choose a preset ($10 = 1,000 credits) or enter a custom amount (min $1 = 100 credits, max $500). Credits always equal the amount in cents.
  2. Submit to create a pending payment and receive a unique reference like ZN-XXXXXXXX.
  3. Pay by bank transfer or mobile money using the instructions shown, quoting your reference.
  4. An admin approves the payment; credits are added exactly once and appear in your balance and ledger.

Pay instantly with MTN MoMo. You can also approve a mobile-money request on your phone via IntouchPay or KitovuPay. Your payment is confirmed automatically — no admin review and no proof upload — and the RWF amount is computed at the operator rate shown on the billing page.

Pending requests can be cancelled before approval. Manual (admin-approved) payments and the instant mobile-money gateways are documented here; API v1 is unchanged.

Dedicated sender name is an optional add-on ($10.00 / 30 days) that lets you set the WhatsApp profile name your recipients see, from Dashboard → WhatsApp. It is separate from message credits — messages are still billed at $0.01 each ($10 = 1,000) — and attachments/media are available without it. Buy or renew it from Dashboard → Billing; it uses the same manual payment + admin approval flow, and a renewal extends from your current expiry. It is a dashboard feature only (no API v1 change).

Need help with the name? Contact support and we can set or correct the sender name for you from the admin console — no add-on purchase is required for a support override. Your session must be connected for the change to apply, and it updates the profile name on your WhatsApp account (including on your phone).

Integration service

Need it wired into your product? Add the done-for-you integration service for a one-time $20.00. Submit your requirements and contact channel from the Billing page. You get a pending $20 payment with its own reference; once approved, the order moves to paid and our team starts work. Order progress is visible on the billing page (requested → paid → in_progress → delivered).

Authentication

Create an API key in the dashboard, then send it on every request using either header:

Authorization: Bearer zn_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# or
X-API-Key: zn_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Keys are shown once at creation and stored only as a SHA-256 hash. Revoke a key any time from Dashboard → API Keys.

Account recovery: if you forget your password, use Forgot password? on the sign-in page. Reset links are single-use and expire after 30 minutes, and a successful reset signs out all active sessions. (Email delivery is operator-configured; contact support if a link does not arrive.)

GET

/api/v1/status

Returns your account status, credit balance and WhatsApp connection state.

curl https://znotice.consultbofa.com/api/v1/status \
  -H "Authorization: Bearer $ZNOTICE_API_KEY"
{
  "ok": true,
  "api_version": "v1",
  "user": { "id": 1, "name": "Jane", "email": "jane@example.com" },
  "credits": 1000,
  "whatsapp": { "session": "zn_1", "status": "disconnected", "phone_number": null },
  "api_keys_active": 1,
  "key": { "label": "Default", "prefix": "zn_live_abcd" }
}
POST

/api/v1/send

Send a WhatsApp message. Costs 1 credit per message. Requires a connected WhatsApp session.

curl -X POST https://znotice.consultbofa.com/api/v1/send \
  -H "Authorization: Bearer $ZNOTICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"phone":"250788123456","message":"Hello from ZNotice"}'

Body: phone (country-coded digits, or a national number starting with 0 to use the default country code), message (1–4096 chars), optional media_url (max 500 chars; the message becomes the caption) and optional scheduled_at to queue the send (see below).

media_url: public http(s) URL on port 80/443. ZNotice fetches the file server-side behind an egress guard: private, loopback, link-local and reserved addresses are blocked, DNS is pinned, redirects are capped at 3 (each re-validated), timeout 30s, max 16MB, and only image/*, video/*, audio/* or application/pdf are accepted. SVG (image/svg+xml, image/svg) and any +xml content type are explicitly rejected.

{ "ok": true, "id": 42, "message_id": "true_250788123456@c.us_3EB0...", "credits_remaining": 999 }

If the engine rejects the send, the credit is automatically refunded and a failed message row is stored.

Scheduled & bulk sending

Add scheduled_at to POST /api/v1/send to queue one message, or use POST /api/v1/bulk to queue one message per recipient as a batch. Timestamps are UTC: ISO-8601 with Z/offset, or YYYY-MM-DDTHH:MM. They must be at least 1 minute ahead and no more than 90 days out. Omitting scheduled_at sends on the next worker tick.

curl -X POST https://znotice.consultbofa.com/api/v1/bulk \
  -H "Authorization: Bearer $ZNOTICE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "label": "September promo",
    "message": "Hi {{name}}, your order is ready.",
    "scheduled_at": "2026-09-12T09:30",
    "recipients": [
      { "to": "0788123456", "name": "Alice" },
      { "to": "+250788654321" }
    ]
  }'
{ "ok": true, "batch_id": 8, "total": 2, "duplicates_removed": 0,
  "scheduled_at": "2026-09-12T09:30:00.000Z", "credits_balance": 1000 }

Up to 1,000 recipients per bulk job (default; operator-configurable). Recipients are normalized and deduplicated; any invalid entry rejects the whole request with 400 invalid_recipients and up to 10 {line, value} details. {{name}} and {{phone}} are resolved per recipient at enqueue; other tokens are left untouched.

Credits: no credit is charged at enqueue. Each message is charged 1 credit when it is actually sent and is auto-refunded if the engine rejects it. A queued job can fail item-by-item with insufficient_credits if the balance is spent before dispatch (no credits are lost).

Queue & batch management

EndpointPurpose
GET /api/v1/scheduledOpen queue (pending + sending); optional ?status= and ?limit= (1–200).
GET /api/v1/scheduled/:idFetch one scheduled item.
DELETE /api/v1/scheduled/:idCancel a pending item.
GET /api/v1/batchesList batches.
GET /api/v1/batches/:idBatch counters + items (?limit= 1–500).
DELETE /api/v1/batches/:idCancel a batch's remaining pending items.

Message templates

Save reusable message snippets from Dashboard → Templates and prefill the WhatsApp test form or the scheduler with them. Templates are private to your account and are a dashboard convenience only — there are no template endpoints in API v1, and the API is unchanged.

  • Each template has a name (up to 80 characters, unique per account), a message body (up to 4,096 characters) and an optional media URL (public http(s), up to 500 characters).
  • Use Use for test message or Use in scheduler on the Templates page (or add ?template=<id> to the WhatsApp or Scheduled page) to prefill the form.
  • {{name}} and {{phone}} placeholders are resolved per recipient at send time, exactly as for scheduled and bulk sends.
  • Accounts can save up to 200 templates (operator-configurable), and template names must be unique per account — delete one to free a name or a slot.

Templates only fill the form — sending still costs 1 credit per message and requires a connected WhatsApp session.

GET

/api/v1/messages

List your recent messages. Optional ?limit=50 (1–200).

curl "https://znotice.consultbofa.com/api/v1/messages?limit=20" \
  -H "X-API-Key: $ZNOTICE_API_KEY"
{
  "ok": true,
  "count": 2,
  "data": [
    { "id": 42, "to_number": "250788123456", "body": "Hello from ZNotice",
      "media_url": null, "status": "sent", "wpp_message_id": "…", "credits_charged": 1,
      "created_at": "2026-09-10T12:00:00.000Z" }
  ]
}
GET

/api/v1/usage

Per-day message activity, delivery totals and credits spent for your account, plus a roll-up. Optional ?days=30 (1–90; missing or out-of-range values are clamped, never an error). Read-only — this endpoint never changes your data.

curl "https://znotice.consultbofa.com/api/v1/usage?days=7" \
  -H "Authorization: Bearer $ZNOTICE_API_KEY"
{
  "ok": true,
  "days": 7,
  "data": [
    { "date": "2026-09-05", "sent": 10, "delivered": 5, "read": 3, "failed": 1,
      "total": 19, "credits_spent": 18 }
  ],
  "totals": {
    "sent": 14, "delivered": 7, "read": 4, "failed": 1,
    "total": 26, "credits_spent": 25, "success_rate": 0.9615
  }
}

Days are UTC calendar buckets and the response always contains exactly days points (oldest first, ending today), zero-filled when there is no activity. total is sent + delivered + read + failed; success_rate is a 0–1 ratio over those completed messages. Queued messages are excluded from the counts and from credits_spent.

Outbound webhooks

Subscribe from Dashboard → Webhooks and ZNotice will POST a signed JSON event whenever one of your messages changes state. Endpoints must be public http(s) URLs on port 80/443; loopback, private, link-local and reserved addresses are rejected.

EventFired when
message.sentEngine accepted the send.
message.failedEngine rejected the send (credit refunded).
message.deliveredDelivered to the recipient's device.
message.readRead/played by the recipient.
batch.completedA scheduled/bulk batch finished with no cancellations (emitted once).
test.pingManual test event from the dashboard.
POST /your/endpoint
Content-Type: application/json
User-Agent: ZNotice-Webhooks/1.0
X-ZNotice-Event: message.delivered
X-ZNotice-Signature: sha256=<hex hmac_sha256(secret, rawBody)>

{ "event": "message.delivered", "created_at": "…",
  "data": { "message_id": 42, "to_number": "250788123456", "status": "delivered", "ack": 2 } }

Any 2xx is success. Non-2xx/network errors retry with backoff (+1 min, then +5 min); after 3 failed attempts the delivery is marked failed. Requests time out after 10s.

Verify the signature (Node.js)

const crypto = require('crypto');

function verify(rawBody, header, secret) {
  const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
  const a = Buffer.from(String(header || ''));
  const b = Buffer.from(expected);
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Rate limits

ScopeLimitNotes
Sends per API key60 / minuteConfigurable by the operator; returns 429 rate_limited with retry_after.
Bulk recipients1,000 / jobOperator-configurable; over the cap returns 400 too_many_recipients.
Schedule horizon1 minute – 90 days aheadOutside this window returns 400 invalid_scheduled_at.
Pending scheduled queue5,000 / userOperator-configurable; over the cap returns 429 queue_full.
All API requests120 / minute / IPGlobal API ceiling; returns 429 JSON.
Login / register10 / 15 minutes / IPApplies to the web dashboard only.
Password change5 / 15 minutes / IP + userApplies to the web dashboard only.
Concurrent WhatsApp sessions15Gateway capacity; connecting beyond it returns a friendly capacity message.

Errors

Errors return a JSON object with ok:false and an error code.

StatusCodeMeaning
401missing_api_keyNo key supplied.
401invalid_api_keyKey not recognised.
401revoked_api_keyKey was revoked.
403account_suspendedAccount is not active.
409whatsapp_not_connectedNo connected WhatsApp session.
409not_pending / not_cancellableCancel target is no longer cancellable (already sending or terminal).
409at_capacityGateway session slots are full (dashboard connect only).
402insufficient_creditsNot enough credits to send.
400invalid_phone / message_required / message_too_long / invalid_jsonMalformed request body.
400invalid_scheduled_at / recipients_required / too_many_recipients / invalid_statusMalformed scheduled/bulk request.
400invalid_recipientsOne or more bulk recipients failed normalization (up to 10 {line,value} details).
400invalid_media_urlMedia URL failed the egress guard (scheme, port, non-public target, redirects, size, timeout, content type or the 500-char limit).
413payload_too_largeRequest body exceeds the 1MB limit.
429rate_limitedToo many sends (per-key limit, default 60/min). Includes retry_after.
429queue_fullPer-user pending scheduled queue cap reached.
502send_failedEngine rejected the send; the credit is refunded.
500server_errorUnexpected server error.