# vCX WhatsApp Onboarding & Messaging

## [![Co-existence.png](https://docs.versalence.online/uploads/images/gallery/2026-07/scaled-1680-/LNJ4i3znkroBlq3J-co-existence.png)](https://docs.versalence.online/uploads/images/gallery/2026-07/LNJ4i3znkroBlq3J-co-existence.png)Base URL - Administration

```
https://backend.admin.versalence.online/api/v2
```

All protected endpoints require:

```
Authorization: Bearer <JWT_TOKEN>
```

---

## 1. Authenticate the customer admin

The customer’s admin user logs in with their vCX credentials. The returned JWT is used for all subsequent calls.

```
POST /api/v2/login
Content-Type: application/json

{
  "email": "admin@customer.com",
  "password": "CustomerAdminPassword"
}
```

Response:

```
{
  "success": true,
  "message": "Sign-in successful",
  "token": "<JWT_TOKEN>"
}
```

**Notes:**

- The user must have `email_verified = 'yes'`.
- The account must be `active`.
- The JWT expiry is controlled by the server (`JWT_EXPIRES_IN`).
- The JWT contains the company `uuid`. The backend uses this to identify which company to onboard.

---

## Create a Sub-Account Under an Agency

An agency account creates a sub-account by calling the standard onboarding endpoint. The agency relationship is **implicitly derived from the JWT**; there is no body parameter to set the parent agency UUID.

<div class="container" id="bkmrk-post-%2Fapi%2Fv2%2Fadduser"><div class="endpoint-card"><div class="endpoint-title"><span class="badge method-post">POST</span> /api/v2/addUser <span class="badge role-agency">Agency Account</span></div></div></div>### Headers

```
Authorization: Bearer <agency-account-jwt>
Content-Type: application/json
```

### How the Agency Tag Is Derived

<div class="container" id="bkmrk-the-jwt-uuid-claim-i"><div class="endpoint-card">1. The JWT `uuid` claim identifies the calling account.
2. The `cust_auth.authenticate` middleware validates the token and calls `MiddlewareCheck` to verify that the UUID belongs to a company with `is_agency = 'yes'`.
3. If valid, the middleware sets `req.parent_uuid` to the agency UUID.
4. The `SignUpController` passes that UUID as `agent_uuid` to the creation service, which writes it into `company.parent_agency_uuid`.

</div></div>### Request Body

```
{
  "name": "Sub-account Admin Name",
  "email": "admin@subaccount.com",
  "password": "securePassword",
  "company_name": "Sub Account Inc",
  "company_email": "info@subaccount.com",
  "company_type": "Agency Client",
  "phone_number": "1234567890",
  "address": "123 Main St",
  "industry": "Technology",
  "website": "https://subaccount.com"
}
```

### Required Fields

<div class="container" id="bkmrk-field-validation-nam"><div class="endpoint-card"><table><thead><tr><th>Field</th><th>Validation</th></tr></thead><tbody><tr><td>`name`</td><td class="field-required">Required</td></tr><tr><td>`company_name`</td><td class="field-required">Required</td></tr><tr><td>`company_type`</td><td class="field-required">Required</td></tr><tr><td>`email`</td><td class="field-required">Required, valid email</td></tr><tr><td>`company_email`</td><td class="field-required">Required, valid email</td></tr><tr><td>`password`</td><td class="field-required">Required, 6–26 characters</td></tr><tr><td>`phone_number`</td><td class="field-required">Required, 8–15 characters</td></tr><tr><td>`address`</td><td>Optional</td></tr><tr><td>`industry`</td><td>Optional</td></tr><tr><td>`website`</td><td>Optional</td></tr></tbody></table>

</div></div>### Success Response — 201

```
{
  "success": true,
  "message": "User created successfully."
}
```

### What Happens on the Backend

<div class="container" id="bkmrk-a-new-uuid-and-compa"><div class="endpoint-card">- A new UUID and `company_id` are generated.
- Records are inserted into `uuid_table`, `company`, and `company_users`.
- `company.parent_agency_uuid` is set to the agency UUID from the JWT.
- The first user is created with `user_role = 'admin'`, `email_verified = 'yes'`, and `first_admin = 'yes'`.
- Plan assignment: if the creator is not the Versalence super-account, the sub-account inherits the agency's current `company_plan_id` from `company_subscription_current`; otherwise it falls back to `plan0`.

</div></div>### Error Responses

<div class="container" id="bkmrk-status-meaning-front-1"><div class="endpoint-card"><table><thead><tr><th>Status</th><th>Meaning</th><th>Frontend Handling</th></tr></thead><tbody><tr><td>`400`</td><td>Validation failed or duplicate user/company email</td><td>Show inline field errors</td></tr><tr><td>`403`</td><td>Authorization header missing, invalid token, or account is not an agency</td><td>Redirect to login or show access denied</td></tr><tr><td>`500`</td><td>Unexpected creation error</td><td>Show generic error and allow retry</td></tr></tbody></table>

</div></div>## UI / Frontend Notes

<div class="container" id="bkmrk-note%3A-the-adduser-en"><div class="note">**Note:** The `addUser` endpoint does **not** accept a parent agency UUID in the body. The frontend should only allow this action when the logged-in account is an agency, and it should rely on the agency's JWT to establish the relationship automatically.</div><div class="warning">**Warning:** Only accounts with `is_agency = yes` can create sub-accounts via this flow. The configured `VERSALENCE_UUID` super-account is the only exception and can bypass the agency check.</div></div>### Recommended Page Structure

```
SuperAdminPanel
└── AgencyManagementPage
    ├── AccountSearch / AccountSelector
    ├── AccountDetailsCard
    │   ├── CurrentAgencyStatusBadge
    │   └── PromoteButton / DemoteButton
    └── ConfirmAgencyStatusModal

AgencyDashboard
└── SubAccountManagementPage
    ├── SubAccountList
    └── CreateSubAccountForm (calls POST /api/v2/addUser)
```

## Environment Variables

<div class="container" id="bkmrk-variable-purpose-sup"><table><thead><tr><th>Variable</th><th>Purpose</th></tr></thead><tbody><tr><td>`SUPER_ADMIN_UUID`</td><td>Overrides the default super admin UUID.</td></tr><tr><td>`VERSALENCE_UUID`</td><td>Bypasses the agency check for the master account.</td></tr></tbody></table>

</div>## 2. Get Agency Customers

```markdown
GET /api/admin/v2/getCustomers
Authorization: Bearer <agency-jwt>
```

Response:

```json
{
  "success": true,
  "message": "Customers retrieved successfully.",
  "data": [
    {
      "uuid": "...",
      "company_name": "...",
      "company_email": "...",
      "company_id": "AGTSW000012026",
      "account_status": "active",
      "parent_agency_uuid": "...",
      "company_member_since": "...",
      "user_name": "...",
      "user_email": "...",
      "user_phone": "...",
      "user_role": "admin",
      "user_status": "active"
    }
  ]
}
```

---

## 3. Login to Sub Account using company\_id

```markdown
GET /api/admin/v2/clientAgentLogin?client_id=<SUB_ACCOUNT_COMPANY_ID>
Authorization: Bearer <agency-owner-jwt>
```

#### How it works

1. The agency owner logs in via `/api/admin/v2/adminLogin` to get their own JWT.
2. They call `/api/admin/v2/clientAgentLogin?client_id=<company_id>` with that JWT.
3. The backend validates that the requested account is actually a sub-account under the agency.
4. It returns a JWT for the sub-account’s first admin user.

#### Example

```markdown
GET /api/admin/v2/clientAgentLogin?client_id=AGTSW000012026
Authorization: Bearer <agency-owner-jwt>
```

Response:

```json
{
  "success": true,
  "message": "User successfully logged in.",
  "token": "<sub-account-jwt>"
}
```

---

## Base URL - Backend

```
https://backend.versalence.online
```

All protected endpoints require:

```
Authorization: Bearer <JWT_TOKEN>
```

---

## 4. Check WhatsApp integration status

Use this to decide whether the customer still needs to onboard WhatsApp.

```
GET https://backend.versalence.online/api/v2/getWaba
Authorization: Bearer <JWT_TOKEN>
```

**Not integrated response:**

```
{
  "success": true,
  "message": "no whatsapp data found"
}
```

**Already integrated response:**

```
{
  "success": true,
  "message": "whatsapp data found",
  "data": [
    {
      "app_id": "...",
      "waba_id": "...",
      "access_token": "[hidden]",
      "phone_id": "...",
      "phone_number": "919876543210",
      "display_phone_number": "+91 98765 43210",
      "onboarding_mode": "standard",
      "is_active": true,
      "webhook_subscription_status": "subscribed"
    }
  ]
}
```

---

## 5. Launch Meta Embedded Signup

The mobile app never sees the Meta App ID. It asks the vCX backend for the OAuth URL, opens it in a browser, and polls for the result after Meta redirects back to vCX.

### 5a. Initiate WhatsApp Embedded Signup

```
GET https://backend.versalence.online/api/v2/whatsappSignupInit?mode=coexistence
Authorization: Bearer <vCX_JWT_TOKEN>
```

Query parameters:

<table id="bkmrk-parameter-required-v"><thead><tr><th>Parameter</th><th>Required</th><th>Values</th><th>Default</th><th>Description</th></tr></thead><tbody><tr><td>`mode`</td><td>No</td><td>`standard`, `coexistence`</td><td>`standard`</td><td>WhatsApp onboarding mode. Use `coexistence` if the customer wants to keep their existing WhatsApp Business app running alongside vCX.</td></tr></tbody></table>

Response:

```
{
  "success": true,
  "data": {
    "oauth_url": "https://www.facebook.com/v20.0/dialog/oauth?client_id=...&redirect_uri=...&scope=whatsapp_business_management&response_type=code&state=...",
    "state": "<STATE_TOKEN>",
    "redirect_uri": "https://backend.versalence.online/api/v2/whatsappSignupCallback",
    "scope": "whatsapp_business_management",
    "mode": "coexistence"
  }
}
```

**Mobile app action:** Open `oauth_url` in an in-app browser or WebView. Do not extract or store the Meta App ID.

### 5b. Meta OAuth callback

After the user completes Meta’s flow, Meta redirects the browser to the vCX backend. The mobile app does **not** call this endpoint directly.

```
GET https://backend.versalence.online/api/v2/whatsappSignupCallback?code=<META_AUTH_CODE>&state=<STATE_TOKEN>
```

What the backend does:

1. Exchanges the code for a Meta access token.
2. Resolves the WABA ID and phone number.
3. Registers the phone number.
4. Configures the vCX webhook.
5. Persists the account.
6. For `coexistence` mode, requests `smb_app_state_sync` and `history` sync from Meta.

On success, the browser is redirected to:

```
https://backend.versalence.online/whatsapp-signup-success
```

On failure:

```
https://backend.versalence.online/whatsapp-signup-failure?error=<ERROR_MESSAGE>
```

These redirect URLs are configurable via environment variables.

### 5c. Check WhatsApp signup status

The mobile app should poll this endpoint after the browser is redirected back.

```
GET /api/v2/whatsappSignupStatus
Authorization: Bearer <vCX_JWT_TOKEN>
```

**Not connected response:**

```
{
  "success": true,
  "message": "No WhatsApp account connected",
  "data": {
    "is_connected": false,
    "onboarding_mode": "standard"
  }
}
```

**Connected response:**

```
{
  "success": true,
  "message": "WhatsApp account connected",
  "data": {
    "is_connected": true,
    "onboarding_mode": "coexistence",
    "is_active": true,
    "waba_id": "...",
    "phone_id": "...",
    "phone_number": "919876543210",
    "display_phone_number": "+91 98765 43210",
    "webhook_subscription_status": "subscribed",
    "expires_in": "...",
    "last_message_at": null,
    "last_smb_message_echo_at": null,
    "last_history_at": null,
    "last_smb_app_state_sync_at": null
  }
}
```

This endpoint can be queried at any time in the future to check whether WhatsApp is still connected.

---

## 6. Get messaging API configuration

```
GET /api/v2/whatsappApiConfig
Authorization: Bearer <vCX_JWT_TOKEN>
```

Response:

```
{
  "success": true,
  "data": {
    "send_messages_url": "https://api.versal.one",
    "create_templates_url": "https://api.versal.one",
    "message_status_url": "https://apiproxy.versal.one"
  }
}
```

Use these endpoints (documented separately) to send messages, create templates, and check message delivery status.

---

## 7. Existing account lookup (backward-compatible)

```
GET /api/v2/getWaba
Authorization: Bearer <vCX_JWT_TOKEN>>
```

This existing endpoint continues to work and returns the connected WhatsApp account data. The mobile app may use either `/getWaba` or `/whatsappSignupStatus`.

## Onboarding modes

### Standard mode

```
GET /api/v2/whatsappSignupInit?mode=standard
```

- Uses the Meta Cloud API onboarding.
- The customer’s WhatsApp Business app will be replaced by vCX for business messaging.Coexistence mode

```
GET /api/v2/whatsappSignupInit?mode=coexistence
```

- The customer’s existing WhatsApp Business app continues to work.
- vCX receives messages through webhooks while the customer keeps using their native WhatsApp Business app.
- The backend requests `smb_app_state_sync` and `history` sync from Meta during onboarding.

#### Environment variables

Add these to the vCX backend `.env`:

```
# Backend-driven OAuth signup flow for external mobile apps.
# WHATSAPP_OAUTH_REDIRECT_URI must be registered in the Meta app settings.
WHATSAPP_OAUTH_REDIRECT_URI=https://backend.versalence.online/api/v2/whatsappSignupCallback
WHATSAPP_OAUTH_SUCCESS_REDIRECT=https://backend.versalence.online/whatsapp-signup-success
WHATSAPP_OAUTH_FAILURE_REDIRECT=https://backend.versalence.online/whatsapp-signup-failure

# External WhatsApp messaging API endpoints exposed to mobile apps.
WHATSAPP_SEND_MESSAGES_URL=https://api.versal.one
WHATSAPP_CREATE_TEMPLATES_URL=https://api.versal.one
WHATSAPP_MESSAGE_STATUS_URL=https://apiproxy.versal.one
```

The following existing variables are also used:

```
APP_ID=
APP_SECRET=
META_API_VERSION=
```

#### Prerequisites

1. The customer account must already exist in vCX (created as a sub-account under the partner’s master account).
2. The customer must have an admin user with verified email and active account status.
3. The `WHATSAPP_OAUTH_REDIRECT_URI` must be registered in the Meta app settings.
4. Migration `009_add_whatsapp_coexistence_support.sql` must be applied to production. It creates the `onboarding_sessions` and `whatsapp_accounts` tables required by the new flow.

#### Coexistence with the existing web dashboard flow

The existing web dashboard flow is unchanged and continues to work:

- `POST /v2/whatsappSign`
- `POST /v2/whatsappWaba`
- `POST /v2/whatsappPhone`
- `POST /v2/embeddedSignup`
- `GET /v2/getWaba`
- `GET /v2/whatsapp-coexistence/status`

The new mobile-app flow is additive:

- <span style="background-color: rgb(248, 248, 248); font-family: 'Lucida Console', 'DejaVu Sans Mono', 'Ubuntu Mono', Monaco, monospace; font-size: 0.84em; white-space: pre-wrap;">GET /v2/whatsappSignupInit</span>
- `GET /v2/whatsappSignupCallback`
- `GET /v2/whatsappSignupStatus`
- `GET /v2/whatsappApiConfig`

The removed `/v2/whatsapp-coexistence/enable` and `/v2/whatsapp-coexistence/disable` endpoints are no longer needed because the mode is passed directly at signup time.

## Error handling

<table id="bkmrk-step-typical-failure"><thead><tr><th>Step</th><th>Typical failure</th><th>How the mobile app should handle</th></tr></thead><tbody><tr><td>Login</td><td>Invalid credentials</td><td>Show error and ask user to retry.</td></tr><tr><td>Initiate signup</td><td>Missing `APP_ID` / redirect URI</td><td>Backend misconfiguration. Contact vCX support.</td></tr><tr><td>Meta OAuth</td><td>User cancels</td><td>Browser redirects to failure page. Poll `/whatsappSignupStatus` to confirm not connected.</td></tr><tr><td>Callback</td><td>Invalid/expired `state`</td><td>Browser redirects to failure page with `error=Invalid or expired onboarding session`.</td></tr><tr><td>Callback</td><td>Missing WABA or phone number</td><td>Browser redirects to failure page. User must retry signup.</td></tr><tr><td>Status polling</td><td>`is_connected: false`</td><td>Continue polling or restart signup.</td></tr></tbody></table>

#### Summary of API endpoints

<table id="bkmrk-endpoint-method-auth"><thead><tr><th>Endpoint</th><th>Method</th><th>Auth</th><th>Purpose</th></tr></thead><tbody><tr><td>`/api/v2/login`</td><td>POST</td><td>Public</td><td>Authenticate and get JWT.</td></tr><tr><td>`/api/admin/v2/adminLogin`</td><td>POST</td><td>Public</td><td>Agency owner login.</td></tr><tr><td>`/api/admin/v2/getCustomers`</td><td>GET</td><td>Agency JWT</td><td>List sub-accounts.</td></tr><tr><td>`/api/admin/v2/clientAgentLogin`</td><td>GET</td><td>Agency JWT</td><td>Get sub-account JWT.</td></tr><tr><td>`/api/v2/getWaba`</td><td>GET</td><td>Any JWT</td><td>Check WhatsApp account.</td></tr><tr><td>`/api/v2/whatsappSignupInit`</td><td>GET</td><td>Admin JWT</td><td>Get Meta OAuth URL.</td></tr><tr><td>`/api/v2/whatsappSignupCallback`</td><td>GET</td><td>None (Meta calls this)</td><td>Complete signup after Meta OAuth.</td></tr><tr><td>`/api/v2/whatsappSignupStatus`</td><td>GET</td><td>Any JWT</td><td>Check if WhatsApp is connected.</td></tr><tr><td>`/api/v2/whatsappApiConfig`</td><td>GET</td><td>Any JWT</td><td>Get messaging API URLs.</td></tr></tbody></table>

## Appendix: Web dashboard 3-step flow

The following endpoints are used by the existing vCX web dashboard. They remain available but are **not** used by the mobile-app OAuth flow described above.

### A1. Exchange code for access token

```
POST /api/v2/whatsappSign
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json

{
  "code": "<META_AUTH_CODE>",
  "mode": "coexistence"
}
```

Response:

```
{
  "success": true,
  "message": "Access token updated successfully",
  "session_id": "<SESSION_ID>",
  "mode": "coexistence",
  "data": "<ACCESS_TOKEN>"
}
```

Save `session_id` and discard `data` (legacy field).

### A2. Resolve WABA ID

```
POST /api/v2/whatsappWaba
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json

{
  "session_id": "<SESSION_ID>"
}
```

Response:

```
{
  "success": true,
  "message": "WABA ID updated successfully",
  "data": {
    "waba_id": "<WABA_ID>"
  },
  "session_id": "<SESSION_ID>"
}
```

### A3. Resolve phone number and complete signup

```
POST /api/v2/whatsappPhone
Authorization: Bearer <JWT_TOKEN>
Content-Type: application/json

{
  "session_id": "<SESSION_ID>"
}
```

``

Response:

``

```
{
  "success": true,
  "message": "Whatsapp embedded signup complete with co-existence mode",
  "mode": "coexistence",
  "phone_number": "919876543210",
  "phone_id": "<PHONE_ID>",
  "webhook_config": "Webhook configured successfully"
}
```

``

**Role requirement:** Admin only for all three steps.

## Send messages via vCX

Once onboarding is complete, use the separately documented messaging API to send WhatsApp messages through the vCX platform.

The backend stores the access token and phone ID, so the send-message API only needs the vCX customer JWT (and the recipient/message payload).

## Important notes

``

- **Legacy endpoint `POST /api/v2/embeddedSignup`** exists as a single-call onboarding flow, but it always uses `standard` mode and does not support Coexistence. Use the 3-step flow in the appendix for Coexistence via the dashboard.
- **Token storage:** The 3-step flow uses `session_id` to keep the Meta access token server-side. The mobile-app OAuth flow keeps the token entirely server-side.
- **No login to vCX web UI required:** The mobile app can perform the entire flow via the API calls above.