Skip to content
DocsProductSign inCreate an account

Channels

WhatsApp

WhatsApp is the storefront: customers browse, order, pay and track without leaving the chat. The Meta side of it is managed by Gumza, so there is nothing for a merchant to configure — this page covers what you see, and what an operator does to provision a number.

Who does what

PieceOwned by
Meta business app, access token, app secretGumza
Webhook endpoint and its verify tokenGumza
Which number belongs to which storeGumza, provisioned per store
Catalogue, prices, stock, bot copyYou, from the dashboard

What a merchant sees

Settings → WhatsApp in the dashboard is a read-only status page. It shows whether the channel is live, the number customers message, the phone number ID and business account ID for support, the API version in use, and when it was connected.

To change the number, add a second one, or pause ordering, contact support. There is no self-serve write — a merchant pointing their store at a number the platform never issued is exactly the state the read-only page exists to prevent.

  • GET/api/v1/admin/channels/whatsappYour store channel status. Read-only, owner and ops roles

Secrets are never returned by this endpoint — only the channel state and the non-sensitive identifiers above. A store with no channel yet gets a 404, which the dashboard renders as “not connected”.

Requesting a number

  1. Decide whose number it is

    Either Gumza issues one for you, or you bring a number you already own. Bringing your own keeps the number your customers already know, which matters if it is on your packaging and your shopfront.

  2. Free the number, if it is yours

    A number cannot be on the regular WhatsApp or WhatsApp Business consumer apps and on the Business Platform at the same time. Delete it from the consumer app first and let the deregistration settle before handing it over — this is the step that most often delays a launch by a day.

  3. Hand it to your operator

    They provision it against your store. One number maps to exactly one store across the whole platform, so a number already live elsewhere is rejected rather than quietly reassigned.

  4. Test it

    Message the number from your own phone. You should get the welcome reply. Then walk a full order through: browse, add to cart, give an address, pay, and watch the status updates land back in the same thread.


For operators: onboarding a number

The rest of this page is the super-admin path. Credentials are entered per store from the platform store console, or through the tenant-scoped channel endpoints.

  • GET/api/v1/admin/tenants/:tenantId/channel/whatsappRead a store channel
  • PUT/api/v1/admin/tenants/:tenantId/channel/whatsappCreate or update the credentials for that store
  • POST/api/v1/admin/tenants/:tenantId/channel/whatsapp/activeTurn the channel on or off without deleting it
  • DELETE/api/v1/admin/tenants/:tenantId/channel/whatsappRemove the channel and its stored credentials

The four values

ValueWhere it comes fromUsed for
phoneNumberIdWhatsApp Manager → API SetupRouting an inbound message to the right store
accessTokenSystem user token on the Meta appSending every outbound message
appSecretApp → Settings → BasicVerifying the signature on every inbound webhook
webhookVerifyTokenYou generate it — 8 characters or moreThe one-time handshake when Meta registers the webhook
businessAccountIdWhatsApp Manager (optional)Support and troubleshooting
displayPhoneNumberThe number customers see (optional)Shown to the merchant so staff know which line is live
Generate a verify token
node -e "console.log(require('crypto').randomBytes(24).toString('base64url'))"

Two uniqueness rules are enforced platform-wide, both with a clear conflict error: a phone number ID can belong to only one store, and a verify token can belong to only one channel. The second is not cosmetic — the Meta handshake is answered by looking the token up, so a shared token would be ambiguous.

PUT — first-time onboarding
curl -X PUT https://gumza.ke/api/v1/admin/tenants/$TENANT_ID/channel/whatsapp \
  -H "Authorization: Bearer $SUPER_ADMIN_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "phoneNumberId": "123456789012345",
    "businessAccountId": "098765432109876",
    "displayPhoneNumber": "+254700000000",
    "accessToken": "EAAG…",
    "appSecret": "…",
    "webhookVerifyToken": "…",
    "apiVersion": "v18.0"
  }'

On a later update, send only what changed. Any secret you omit keeps the value already stored, so the access token can be rolled without re-entering the app secret. First-time onboarding is the exception: all three secrets must be present, because there is nothing stored to fall back on.

Credentials are encrypted with AES-256-GCM before they reach the database and decrypted only on the hot path — sending a message, or checking an inbound signature. They are never returned by any endpoint and never written to a log. Losing CHANNEL_ENCRYPTION_KEY permanently locks every stored credential on that deployment, so it belongs in a secret manager with a backup, not only in an env file.

Point Meta at the webhook

SettingValue
Callback URLhttps://gumza.ke/webhooks/whatsapp
Verify tokenThe token stored on the channel
Subscribe tomessages

Inbound messages then arrive as POST to the same URL. The payload names its own phone_number_id, which is how one webhook URL serves every store: we use it to find the channel, then verify X-Hub-Signature-256 against that store's app secret before reading a single field.

When it goes quiet

SymptomUsual cause
Meta will not verify the callbackChannel not saved yet, or the verify token differs by a character
Messages arrive but no reply is sentAccess token expired — mint a permanent system-user token
Nothing arrives at allNot subscribed to the messages field, or the channel is inactive
Inbound rejected as unauthorisedApp secret belongs to a different Meta app than the one sending
Phone number already onboardedThe number is live on another store — one number, one store

Once orders are flowing, make sure they get delivered — see Set up on Gumza for the delivery link, or Webhooks to get the same status events into your own system.