kenari.dev

Quickstart#

This guide takes you from a new account to a delivered WhatsApp message. You need a WhatsApp number you can connect to Meta, and a phone with WhatsApp to receive the test message.

1. Create an account#

Sign up at app.kenari.dev/signup and verify your email address. New accounts start with a 7-day trial.

2. Connect a number#

Open Numbers in the dashboard. There are two ways to connect:

  • Embedded Signup. Select Continue with Facebook, sign in to Meta, pick or create a WhatsApp Business account and choose the phone number to connect. Grant both WhatsApp permissions (management and messaging) when Meta asks.
  • Bring your own account. If you already have a Meta app and a system user token, enter the WABA ID, phone number ID, access token, app ID and app secret. The dashboard then shows a callback URL and verify token to set in your Meta app's webhook settings.

When the number shows as Connected, copy its phone number ID. That's Meta's ID for the number, not the phone number itself. You'll put it in the request path.

3. Create an API key#

Open API Keys and select Create API Key. Give the key a name, choose live mode, then select Create key.

The full key is shown once, right after you create it. Copy it and store it somewhere safe, such as your secret manager or a local .env file that isn't committed. kenari stores only a hash of the key and can't show it again.

bash
export KENARI_API_KEY="kn_live_..."export PHONE_NUMBER_ID="123456789012345"

4. Send a text message#

Send a POST to /{version}/{phone-number-id}/messages. The body is Meta's Cloud API message payload. Replace the to number with your own WhatsApp number, in international format without the +.

bash
curl -X POST https://api.kenari.dev/v23.0/$PHONE_NUMBER_ID/messages \  -H "Authorization: Bearer $KENARI_API_KEY" \  -H "Content-Type: application/json" \  -d '{    "messaging_product": "whatsapp",    "to": "6281234567890",    "type": "text",    "text": { "body": "Hello from kenari" }  }'

kenari forwards the request to Meta and returns Meta's response unchanged. A successful send looks like this:

json
{  "messaging_product": "whatsapp",  "contacts": [{ "input": "6281234567890", "wa_id": "6281234567890" }],  "messages": [{ "id": "wamid.HBgM..." }]}

The same request in Node.js:

js
const res = await fetch(`https://api.kenari.dev/v23.0/${process.env.PHONE_NUMBER_ID}/messages`, {  method: "POST",  headers: {    Authorization: `Bearer ${process.env.KENARI_API_KEY}`,    "Content-Type": "application/json",  },  body: JSON.stringify({    messaging_product: "whatsapp",    to: "6281234567890",    type: "text",    text: { body: "Hello from kenari" },  }),});console.log(res.status, await res.json());

If it didn't work#

StatusError typeLikely cause
401KenariAuthExceptionMissing or mistyped key, or a revoked key. A kn_test_ key used on a number that isn't a Meta test number also returns 401.
402KenariBillingExceptionThe account is suspended, its payment is overdue, or it has no active subscription.
404KenariRoutingExceptionThe path isn't /{version}/{id}/messages, or the phone number ID isn't connected to your account.
422KenariNotConnectedExceptionThe number was disconnected, restricted or isn't registered with Meta yet, or the WABA's access was revoked.
OtherMeta's own errorMeta rejected the payload. The body is Meta's error, passed through unchanged.

kenari errors use this envelope. fbtrace_id holds kenari's request ID, the same value as the X-Kenari-Request-Id header:

json
{  "error": {    "message": "Invalid or missing API key.",    "type": "KenariAuthException",    "code": 1001,    "fbtrace_id": "req_..."  }}

Next steps#