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.
- ZNotice Complete Documentation ZNotice-Complete-Documentation-2026-09-18.pdf
- ZNotice API Reference ZNotice-API-Reference-2026-09-18.pdf
- ZNotice Architecture ZNotice-Architecture-2026-09-18.pdf
- ZNotice Business Model ZNotice-Business-Model-2026-09-18.pdf
- ZNotice Changelog ZNotice-Changelog-2026-09-18.pdf
- ZNotice Operations Runbook ZNotice-Operations-Runbook-2026-09-18.pdf
- ZNotice Overview ZNotice-Overview-2026-09-18.pdf
- ZNotice Security ZNotice-Security-2026-09-18.pdf
Credits & billing
Messaging is pay-as-you-go at $0.01 per message ($10 = 1,000 credits). Buy credits from Dashboard → Billing.
- 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.
- Submit to create a pending payment and receive a unique reference like
ZN-XXXXXXXX. - Pay by bank transfer or mobile money using the instructions shown, quoting your reference.
- 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.)
/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" }
}
/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
| Endpoint | Purpose |
|---|---|
GET /api/v1/scheduled | Open queue (pending + sending); optional ?status= and ?limit= (1–200). |
GET /api/v1/scheduled/:id | Fetch one scheduled item. |
DELETE /api/v1/scheduled/:id | Cancel a pending item. |
GET /api/v1/batches | List batches. |
GET /api/v1/batches/:id | Batch counters + items (?limit= 1–500). |
DELETE /api/v1/batches/:id | Cancel 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.
/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" }
]
}
/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.
| Event | Fired when |
|---|---|
message.sent | Engine accepted the send. |
message.failed | Engine rejected the send (credit refunded). |
message.delivered | Delivered to the recipient's device. |
message.read | Read/played by the recipient. |
batch.completed | A scheduled/bulk batch finished with no cancellations (emitted once). |
test.ping | Manual 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
| Scope | Limit | Notes |
|---|---|---|
| Sends per API key | 60 / minute | Configurable by the operator; returns 429 rate_limited with retry_after. |
| Bulk recipients | 1,000 / job | Operator-configurable; over the cap returns 400 too_many_recipients. |
| Schedule horizon | 1 minute – 90 days ahead | Outside this window returns 400 invalid_scheduled_at. |
| Pending scheduled queue | 5,000 / user | Operator-configurable; over the cap returns 429 queue_full. |
| All API requests | 120 / minute / IP | Global API ceiling; returns 429 JSON. |
| Login / register | 10 / 15 minutes / IP | Applies to the web dashboard only. |
| Password change | 5 / 15 minutes / IP + user | Applies to the web dashboard only. |
| Concurrent WhatsApp sessions | 15 | Gateway capacity; connecting beyond it returns a friendly capacity message. |
Errors
Errors return a JSON object with ok:false and an error code.
| Status | Code | Meaning |
|---|---|---|
| 401 | missing_api_key | No key supplied. |
| 401 | invalid_api_key | Key not recognised. |
| 401 | revoked_api_key | Key was revoked. |
| 403 | account_suspended | Account is not active. |
| 409 | whatsapp_not_connected | No connected WhatsApp session. |
| 409 | not_pending / not_cancellable | Cancel target is no longer cancellable (already sending or terminal). |
| 409 | at_capacity | Gateway session slots are full (dashboard connect only). |
| 402 | insufficient_credits | Not enough credits to send. |
| 400 | invalid_phone / message_required / message_too_long / invalid_json | Malformed request body. |
| 400 | invalid_scheduled_at / recipients_required / too_many_recipients / invalid_status | Malformed scheduled/bulk request. |
| 400 | invalid_recipients | One or more bulk recipients failed normalization (up to 10 {line,value} details). |
| 400 | invalid_media_url | Media URL failed the egress guard (scheme, port, non-public target, redirects, size, timeout, content type or the 500-char limit). |
| 413 | payload_too_large | Request body exceeds the 1MB limit. |
| 429 | rate_limited | Too many sends (per-key limit, default 60/min). Includes retry_after. |
| 429 | queue_full | Per-user pending scheduled queue cap reached. |
| 502 | send_failed | Engine rejected the send; the credit is refunded. |
| 500 | server_error | Unexpected server error. |