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.