Introduction#
kenari is a proxy that sits in front of Meta's WhatsApp Cloud API. You connect a WhatsApp number once in the kenari dashboard. After that, your code sends the same Cloud API requests it would send to Meta, but to https://api.kenari.dev and with a kenari API key.
You don't need a Meta app, a system user or a long-lived Meta access token in your code. kenari keeps the Meta credentials for each connected WhatsApp Business account (WABA), sealed at rest, and attaches them to your request on the way out.
What kenari is#
- A proxy for the Cloud API. Paths and JSON bodies are Meta's own:
POST /v23.0/{phone-number-id}/messageswithmessaging_product,to,typeand so on. If Meta's reference documents a field on a supported endpoint, you send it through kenari unchanged. - One key for every number on your account. A kenari API key (
kn_live_…orkn_test_…) authenticates every request. kenari checks that the phone number or WABA in the path belongs to your account before it forwards anything. - A webhook relay. Meta sends events to kenari. kenari signs them and delivers them to the endpoints you register in the dashboard.
What kenari isn't#
- Not a new messaging API. kenari doesn't wrap the Cloud API in its own SDK or payload format. Meta's documentation stays the reference for message payloads.
- Not a full Graph API gateway. Only an allow-list of WhatsApp endpoints is forwarded (messages, media, message templates, the business profile, and reading a phone number). Anything else returns
404withKenariRoutingException. See Supported endpoints. - Not a way around Meta's rules. Meta's messaging policies, template approval, quality ratings and per-number throughput still apply. kenari passes Meta's own errors back to you as they are.
How a request flows#
your app ──► api.kenari.dev ──► graph.facebook.com
kenari key Meta token (added by kenari)For every request kenari:
- Matches the path to
/{version}/{id}or/{version}/{id}/{edge}. Anything else is a404. - Checks the
Authorization: Bearerheader against your API keys. A missing, unknown or revoked key is a401. - Checks that your account can send: a suspended account or one without an active subscription gets a
402. - Checks that the ID in the path is a number, WABA or media object that belongs to your account, and that the method and edge are on the allow-list. If not,
404. - Checks that the number is still connected and registered with Meta. If not,
422. - Applies rate limits (
429when you're over). - Forwards the request to Meta with the Meta token for that WABA and streams Meta's response back to you.
Meta's status code and JSON body come back to you unchanged. Errors kenari raises itself use the Kenari*Exception types described in Errors.
What's different from calling Meta#
| Meta direct | Through kenari | |
|---|---|---|
| Host | https://graph.facebook.com | https://api.kenari.dev |
| Path | /v23.0/{id}/messages | Same |
| Request body | Cloud API JSON | Same |
| Auth header | Authorization: Bearer <Meta token> | Authorization: Bearer <kenari key> |
| Meta app, system user, token renewal | Yours to manage | Handled by kenari |
| Webhooks | Meta calls your server | Meta calls kenari; kenari calls your registered endpoints |
| Endpoints | The whole Graph API | WhatsApp allow-list only |
| Request tracing | x-fb-trace-id | x-fb-trace-id plus X-Kenari-Request-Id |
Next steps#
- Quickstart: connect a number and send your first message.
- Authentication: live and test keys.
- Migrating from Meta direct: move an existing integration over.