Skip to content

Messaging & channels

WhatsApp

Your OnCore assistant answers WhatsApp voice calls and inbound WhatsApp text on a number you already have.

Customers who live in WhatsApp can call you there too. They open your business chat, tap the call button, and your assistant picks up: same assistant, same number, no app switch for them.

Overview#

WhatsApp calling adds a second way to reach the assistant on a phone number you already have with OnCore. Once the number is enabled for WhatsApp, customers can call you from inside the WhatsApp app (no dialling, just a tap in the chat) and the same assistant that answers your phone line takes the call. The number keeps answering ordinary phone calls exactly as before; WhatsApp calling sits alongside, and the call travels over the internet rather than the phone network.

  • Meet customers where they already are: for many customers WhatsApp is the phone; the call button in your chat now reaches a live assistant instead of ringing out.
  • Serve callers abroad without international dialling: a customer in another country taps to call over the internet instead of dialling a foreign number.
  • Keep one assistant across phone and WhatsApp: both routes land on the assistant behind the same number, so its behaviour, greeting, and tasks stay in one place.
  • Enable numbers at scale through the API: start an onboarding with a single request, hand the business owner a signup link, and follow progress by webhook until calling is live; the owner never handles codes or verification screens.
  • Tell WhatsApp calls apart in your reporting: each call is announced to your systems with a whatsapp source marker, alongside the same call records you get for phone calls.

WhatsApp onboarding lets a reseller enable WhatsApp calling on one of a tenant's phone numbers. You start a session, send the end customer to a single-use signup URL where they authenticate with Meta and share their WhatsApp Business Account (WABA), and everything after that (attaching the number, capturing the registration code, enabling calling) happens on OnCore's side. The customer never sees a phone-number or OTP screen.

Once the number is onboarded, inbound WhatsApp text is a separate channel flag on the assistant. Calling and texting share the same WABA number; they do not share the same on/off switch.

In the dashboard: Sign up to WhatsApp on the assistant, then turn WhatsApp Enabled on under Messaging and save.

Authenticate with your tenant API key, or an organization key plus X-Tenant-Id. See Authentication.

Enable or disable the WhatsApp text channel#

Toggle two-way WhatsApp text per assistant. OnCore only answers customer-initiated text. It does not send templates, start a conversation, or handle media.

A WhatsApp text conversation behaves like an SMS conversation: the same opening line, the same ways for a thread to end, and the same handling of a text sent right after a goodbye.

Terminal
curl -X PATCH https://core-api.heysadie.ai/assistants/ASSISTANT_ID/whatsapp-enabled \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "whatsappEnabled": true }'

The response returns the assistant id and its updated featureFlags. When WhatsApp is disabled, OnCore ignores inbound WhatsApp text for the assistant's number(s). Other channel flags (SMS, for example) are left alone.

Enabling requires an onboarded number

Enabling requires a linked phone number that has already been onboarded to WhatsApp (a Meta phone-number ID on the number). If it doesn't, the request fails with 400 (NO_PHONE_NUMBER_LINKED or NUMBER_NOT_WHATSAPP_ONBOARDED) and the flag is left unchanged. Disabling only clears the flag. There is no attach step here: WABA registration happens in the onboarding flow below.

Start a session#

POST /whatsapp/onboarding-sessions requires an Idempotency-Key header:

Terminal
curl -X POST https://core-api.heysadie.ai/whatsapp/onboarding-sessions \
  -H "Authorization: ApiKey YOUR_API_KEY" \
  -H "Idempotency-Key: 8c8f3f1e-YOUR-UNIQUE-KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tenantId": "TENANT_ID",
    "phoneNumber": "+15555550123",
    "displayName": "Acme Support"
  }'
Body fieldRequiredDescription
tenantIdyesTenant (end-business) the number belongs to; must match the caller's API-key tenant
phoneNumberyesE.164 number owned by the tenant, not already WhatsApp-enabled
displayNamenoWhatsApp display name to request for the number (Meta reviews it). Defaults to the tenant name

The response contains a sessionId, a single-use, tenant-bound, 15-minute signupUrl (a OnCore-hosted page wrapping Meta's Embedded Signup) with its expiresAt, and a single-use signup token (also embedded in signupUrl) for resellers hosting their own signup page. Send the end customer to signupUrl.

Idempotency

Re-sending the same Idempotency-Key for the same tenant returns the existing session (same sessionId), not a new one, even for two concurrent requests. A second in-progress onboarding for the same number under a different key is rejected with 409 WHATSAPP_ONBOARDING_IN_PROGRESS.

The Meta consent popup shows the platform provider's registered name, not your customer's business name. Meta does not white-label Tech Providers.

The state machine#

Track progress by polling GET /whatsapp/onboarding-sessions/{id} or by subscribing to the WhatsApp webhook events.

text
pending → authorized → verifying → [tier_pending] → completed → live

with terminal states failed, name_rejected, and abandoned.

StatusMeaning
pendingSession created; waiting for the customer to complete Embedded Signup. Past its 15-minute TTL it is reported as abandoned
authorizedThe customer authorized OnCore; the number is being attached and registered
verifyingA verification call is placing/reading the registration code
tier_pendingNumber verified and registered, but below Meta's calling messaging-limit tier; parked until the limit is raised
completedCalling enabled on the number
liveDisplay name approved and calling is live end-to-end
failed / name_rejected / abandonedTerminal (see failureReason)

On failure states the status response also carries failureReason, blame (customer = the account owner must act, resolve with your customer; system = OnCore/Meta-side, hand to OnCore), and userMessage, human-readable copy safe to show the end customer. When failureReason is otp_request_limit, retryAfterHours gives Meta's lockout window.

A stranded session self-heals to failed past its TTL: failureReason: "stalled" if it crashed in authorized (no verification call placed), or "otp_call_timeout" if it was stuck in verifying. While verifying, if the verification call was placed more than ~5 minutes ago the response carries verificationCallOverdue: true, a soft signal to prompt the customer to resend or cancel.

Resend the verification call#

POST /whatsapp/onboarding-sessions/{id}/resend-code re-triggers Meta's automated voice call that reads the registration code, for a session in verifying. The session stays verifying; otpAttempts in the response reflects the per-session budget consumed so far.

ErrorMeaning
409 WHATSAPP_RESEND_NOT_ALLOWEDThe session is not verifying, or the number is not yet attached
429 WHATSAPP_RESEND_TOO_SOONA call was requested within the last 60 seconds; details.retryAfterSeconds gives the cooldown. No attempt budget consumed
409 WHATSAPP_OTP_ATTEMPTS_EXHAUSTEDThe per-session budget is spent (the session stays verifying, not failed)
429 WHATSAPP_OTP_REQUEST_LIMITMeta's rate limit; details.retryAfterHours gives the cooldown
502 WHATSAPP_RESEND_FAILEDRetryable non-rate-limit Meta/network failure
404Cross-tenant / unknown id (anti-enumeration)

Cancel / reset#

DELETE /whatsapp/onboarding-sessions/{id} is the self-service recovery path:

  • pending / authorized / verifying are cancellable: the number's WhatsApp link is cleared and the session becomes abandoned (a subsequent GET reports failureReason: "cancelled"). The number can immediately be re-onboarded.
  • failed / abandoned / name_rejected are already terminal: idempotent no-op, returns the current status (200).
  • tier_pending / completed / live are not cancellable: a verified/registered or live number returns 409 WHATSAPP_ONBOARDING_NOT_CANCELLABLE.

Cancelling makes no Meta call (a re-onboard idempotently re-attaches the number) and fires no webhook, since you initiated it. Cross-tenant / unknown ids return 404.

Failure reason codes#

On terminal failure, failureReason (and data.reason on the whatsapp.onboarding.failed webhook) is one of:

CodeMeaning
attach_failed / attach_failed:<metaCode>Attaching the number to the WABA failed (e.g. attach_failed:133016)
otp_request_limitMeta registration rate limit (see retryAfterHours)
waba_mismatchThe authorization token does not grant the claimed WABA
voice_routing_missingThe number has no inbound-voice routing to receive the verification call
token_revokedThe customer revoked OnCore's access / the WABA was disabled
name_rejectedMeta rejected the requested display name
authorization_failedToken exchange / post-authorization error
register_failedCloud API registration failed permanently
request_code_failedRequesting the verification code failed (non-rate-limit)
otp_failedVerification failed / attempt budget exhausted
invalid_phone_numberThe stored number is not valid E.164
stalledCrashed in authorized before any verification call; swept to failed past its TTL
otp_call_timeoutStuck in verifying past its TTL (the call was placed but never completed)
whatsapp_waba_owned_by_another_tenantThe claimed WABA is already owned by a different tenant

Webhook events#

Onboarding progress is also pushed to your server as signed webhooks: whatsapp.onboarding.tier_pending, whatsapp.onboarding.completed, whatsapp.onboarding.failed, and whatsapp.calling.live. See WhatsApp events for the payload, signature verification, and retry semantics, and the API reference for the full endpoint specification.

Transfers do not work on WhatsApp calls

transferCall and dynamicTransfer tools are removed before a WhatsApp call starts. The assistant is not told they are missing, so it simply has no way to hand the caller on. It will keep trying to help rather than escalating.

This is easy to miss because it is source-dependent, not configuration-dependent: the same assistant transfers correctly on a phone call and silently cannot on WhatsApp. If an assistant handles both, give it a fallback the caller can use: a number to dial, or a capture form that takes a callback request.

Limits & FAQ#

  • The signup link is single-use and valid for 15 minutes; if it expires or the owner abandons it, you simply start again. Nothing is left in a broken state.
  • The number must already be answering ordinary voice calls: enablement is confirmed by an automated verification call to the line, so a number with no inbound voice routing can't complete onboarding.
  • A brand-new WhatsApp Business Account can sit below Meta's calling tier; the number is parked as tier_pending and calling switches on once the account's limit rises. You're notified by webhook when it completes.
  • WhatsApp calls can't be transferred to your team. Keep lines whose flows depend on call transfers on the regular phone channel.
  • WhatsApp calling and WhatsApp text are separate. Calling goes live through onboarding. Texting stays off until you set whatsappEnabled on the assistant. Stickers and images are ignored; only customer-initiated text is answered.

Is it the same assistant as our phone line? Yes. WhatsApp calls to the number reach the assistant assigned to that number, and regular phone calls continue to work unchanged.

What does the caller see in WhatsApp? Your business display name. You request it during onboarding and Meta reviews it. Once approved, calling is live end-to-end under that name.

What does the business owner have to do? Only the Meta approval: they open the signup link, sign in, and consent. They never see a phone-number or verification-code screen. Note the Meta consent screen shows the platform provider's registered name. Meta does not white-label technology providers.

How do these calls show up in our reporting? Like phone calls: the start-of-call event carries call_source: "whatsapp" (see the assistant-request webhook), and each call delivers an end-of-call report and appears in your call history with its transcript and summary.

Do inbound WhatsApp texts get a reply? Yes, once the number is onboarded and whatsappEnabled is true on the assistant. Threads show up as conversations with channel: whatsapp. OnCore does not start the chat or send templates.

Next steps#