Skip to content

Channels & Embeds

WhatsApp onboarding (resellers)

Enable WhatsApp calling on a tenant's phone number through Meta's Embedded Signup.

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.

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

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_FAILEDNon-rate-limit Meta/network failure — retryable
404Cross-tenant / unknown id (anti-enumeration)

Cancel / reset#

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

  • pending / authorized / verifyingcancellable: 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 — already terminal: idempotent no-op, returns the current status (200).
  • tier_pending / completed / live — a verified/registered or live number is not cancelled here → 409 WHATSAPP_ONBOARDING_NOT_CANCELLABLE.

Cancelling makes no Meta call (a re-onboard idempotently re-attaches the number) and fires no webhook — 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.


New to this? WhatsApp explains what it does and when to use it, without the API detail.