# 8x8 Developer Portal > APIs, SDKs, guides, and integration resources for the 8x8 platform This file contains all documentation content in a single document following the llmstxt.org standard. ## Contact Center Data Augmentation API ## **Overview** The Data Augmentation API allows you to enrich interaction data with custom variables collected from web forms, IVR systems, CRM data, or any external system. This enriched data serves two primary purposes: ### **🎯 Key Use Cases** 1. **Smart Routing**: Use custom variables with the existing **Test Variable** node in IVR scripts to make intelligent routing decisions 2. **Agent Context**: Display valuable customer information in the Agent Workspace when offering interactions *** ## **Authentication** **Method**: HTTP Basic Authentication - **Username**: Your tenant ID - **Password**: Action Request Token (found in Configuration Manager → Integration → API Token) ```http Authorization: Basic {base64encode(tenantId:actionToken)} ``` *** ## **Base URL** ```text https://{cluster}.8x8.com/api/v1/interaction/data ``` | Region | Cluster | Example URL | | ------------- | -------- | -------------------------------------------------- | | North America | vcc-na12 | | | Europe | vcc-eu1 | | | Sandbox | vcc-sb1 | | *** ## **API Endpoints** ### **Set Interaction Variables** **POST** `/api/v1/interaction/data/{identifier}` Add or update custom variables for a specific interaction. #### **Request Body** ```json { "data": [ { "variables": [ { "name": "_customerId", "value": "CUST-123456", "display": true, "displayName": "Customer ID" } ] } ] } ``` #### **Variable Properties** | Property | Type | Required | Description | Constraints | | ------------- | ------- | -------- | ----------------------- | ------------------------------------------------ | | `name` | string | Yes | Variable identifier | Must start with "\_", max 25 chars, alphanumeric | | `value` | string | Yes | Variable value | Max 1000 characters | | `ivr` | boolean | No | Enable for IVR routing | Default: false | | `display` | boolean | No | Show in Agent Workspace | Default: false | | `displayName` | string | No | Agent-facing label | Max 30 characters | | `dataType` | string | No | Data type hint | text, number, phone, date, time, currency | | `privacy` | boolean | No | Mask sensitive data | Default: false | #### **Success Response (200 OK)** ```json { "status": "success", "message": "Variables successfully set", "interactionId": "int-1628bb9dbf7-ABC123", "variablesSet": 3 } ``` ### **Get Interaction Data** **GET** `/api/v1/interaction/data/{identifier}` Retrieve all context data and variables for a specific interaction. #### **Success Response (200 OK)** ```json { "interactionId": "int-1628bb9dbf7-ABC123", "channel": "18005551234", "createTime": "2024-01-15T10:30:00Z", "ani": "16175551234", "callerName": "John Doe", "variables": [ { "name": "_customerId", "value": "CUST-123456", "display": true, "displayName": "Customer ID" } ] } ``` *** ## **Use Case 1: Smart Routing with Test Variable Node** Custom variables can be used with the existing **Test Variable** node in IVR scripts for intelligent routing decisions. ### **How It Works:** 1. Set custom variables via API during the interaction 2. Use the **Test Variable** node in your IVR script 3. Test the variable against specific values 4. Route based on the result (True/False exit points) ### **Example: VIP Customer Routing** ```shell # Set VIP status via API curl -X POST \ 'https://vcc-na12.8x8.com/api/v1/interaction/data/int-123456' \ -H 'Authorization: Basic {credentials}' \ -H 'Content-Type: application/json' \ -d '{ "data": [ { "variables": [ { "name": "_customerTier", "value": "VIP", "ivr": true } ] } ] }' ``` **In IVR Script:** - Add **Test Variable** node - Variable: `_customerTier` - Test Condition: `equals "VIP"` - **True** exit: Route to priority queue - **False** exit: Route to standard queue ### **Example: Account Balance Routing** ```json { "data": [ { "variables": [ { "name": "_accountBalance", "value": "1250.75", "ivr": true, "dataType": "currency" }, { "name": "_paymentDue", "value": "true", "ivr": true } ] } ] } ``` **Test Variable Scenarios:** - Test `_paymentDue` equals "true" → Route to billing department - Test `_accountBalance` less than "0" → Route to collections - Test `_accountBalance` greater than "10000" → Route to VIP support *** ## **Use Case 2: Agent Context Display** Variables with `display: true` appear in the Agent Workspace, providing valuable customer context when interactions are offered. ### **Example: Customer Information Display** ```json { "data": [ { "variables": [ { "name": "_customerId", "value": "CUST-123456", "display": true, "displayName": "Customer ID" }, { "name": "_membershipLevel", "value": "Gold", "display": true, "displayName": "Membership Level" }, { "name": "_lastContactReason", "value": "Billing Inquiry", "display": true, "displayName": "Last Contact Reason" }, { "name": "_accountBalance", "value": "$1,250.75", "display": true, "displayName": "Account Balance", "dataType": "currency" } ] } ] } ``` **Agent Workspace Display:** ```text ┌─────────────────────────────────────┐ │ Customer Information │ ├─────────────────────────────────────┤ │ Customer ID: CUST-123456 │ │ Membership Level: Gold │ │ Last Contact Reason: Billing Inquiry│ │ Account Balance: $1,250.75 │ └─────────────────────────────────────┘ ``` ### **Best Practices for Agent Display:** - Use clear, business-friendly `displayName` values - Limit to 5-7 key variables to avoid information overload - Use appropriate `dataType` for proper formatting - Mark sensitive data with `privacy: true` *** ## **Complete Example: E-commerce Order Support** ```shell curl -X POST \ 'https://vcc-na12.8x8.com/api/v1/interaction/data/int-987654' \ -H 'Authorization: Basic {credentials}' \ -H 'Content-Type: application/json' \ -d '{ "data": [ { "variables": [ { "name": "_orderStatus", "value": "shipped", "ivr": true, "display": true, "displayName": "Order Status" }, { "name": "_orderValue", "value": "299.99", "display": true, "displayName": "Order Value", "dataType": "currency" }, { "name": "_customerTier", "value": "Premium", "ivr": true, "display": true, "displayName": "Customer Tier" }, { "name": "_shippingDate", "value": "2024-07-10", "display": true, "displayName": "Shipping Date", "dataType": "date" } ] } ] }' ``` **Routing Logic:** - Test `_orderStatus` equals "cancelled" → Route to order management - Test `_customerTier` equals "Premium" → Route to priority support - Test `_orderValue` greater than "500" → Route to specialized support **Agent Display:** - Order Status: shipped - Order Value: $299.99 - Customer Tier: Premium - Shipping Date: July 10, 2024 *** ## **Error Responses** | Code | Description | Example Response | | ---- | ------------ | -------------------------------------------------------------------------------------------------- | | 400 | Bad Request | `{"status": "error", "code": 400, "message": "Invalid variable name: must start with underscore"}` | | 401 | Unauthorized | `{"status": "error", "code": 401, "message": "Authentication failed"}` | | 404 | Not Found | `{"status": "error", "code": 404, "message": "Interaction not found"}` | | 429 | Rate Limited | `{"status": "error", "code": 429, "message": "Rate limit exceeded"}` | | 500 | Server Error | `{"status": "error", "code": 500, "message": "Internal server error"}` | *** ## **API Limits** | Limit | Value | | --------------------- | --------------- | | Requests per minute | 100 per tenant | | Variables per request | 50 | | Variable name length | 25 characters | | Variable value length | 1000 characters | | Display name length | 30 characters | *** ## **Security & Best Practices** ### **Security** - **HTTPS Required**: All requests must use HTTPS - **Privacy Protection**: Use `privacy: true` for sensitive data - **Authentication**: Secure your action tokens ### **Best Practices** - **Variable Naming**: Always start with underscore (`_customerId`) - **Data Types**: Use appropriate `dataType` for formatting - **Agent Display**: Limit to essential information for better UX - **Error Handling**: Implement retry logic for 429/500 errors - **Testing**: Use sandbox environment for development ### **Variable Naming Rules** ```text ✅ Good: _customerId, _accountBalance, _orderStatus ❌ Bad: customerId, account-balance, order status ``` *** ## **Quick Reference** ### **Common Variable Examples** ```json { "data": [ { "variables": [ {"name": "_customerId", "value": "CUST-123", "display": true, "displayName": "Customer ID"}, {"name": "_accountBalance", "value": "1250.75", "display": true, "displayName": "Balance", "dataType": "currency"}, {"name": "_membershipLevel", "value": "Gold", "ivr": true, "display": true, "displayName": "Membership"}, {"name": "_lastContactDate", "value": "2024-07-10", "display": true, "displayName": "Last Contact", "dataType": "date"}, {"name": "_accountPin", "value": "1234", "privacy": true, "ivr": true} ] } ] } ``` ### **Test Variable Node Integration** 1. Set variables via API with `"ivr": true` 2. In IVR script, add **Test Variable** node 3. Select your custom variable (e.g., `_customerTier`) 4. Set test condition (equals, greater than, etc.) 5. Connect True/False exits to appropriate routing logic *** ## **Support Resources** - **8x8 Developer Portal**: - **Support Center**: - **Status Page**: - **Platform URL Guide**: --- ## API Key ## Procedure You can obtain an API key using your [8x8 Admin Console](https://admin.8x8.com/) implementation. ![image](../images/8f03ee4-Screenshot_2021-07-05_at_14.54.34.png "Screenshot 2021-07-05 at 14.54.34.png") To obtain your 8x8 API key: 1. Access your 8x8 Admin Console implementation. 2. Click **API Keys** 3. Click **Create App** ![image](../images/7d82f8d-Screenshot_2021-07-05_at_14.57.01.png "Screenshot 2021-07-05 at 14.57.01.png") 4. Add your Application. * Enter an application name * Under API products select **Chat Gateway** to create an API key enabled for the Chat Gateway and click Save. ![API Key](../images/8edbb14-APIKey.png "APIKey.png") * You can access the token, by clicking on the eye icon ![image](../images/ec04df6-optj13.png "optj13.png") ## Or click on the key Then it opens and you can use the eye icon, then copy. . You DO NOT need the secret. ![Iconclick](../images/876f340-Iconclick.png "Iconclick.png") --- ## Button theming ## Primary and secondary buttons You can change both the primary and secondary colours on the chat widget, either globally across each of the parts of the widget — invitation/form/window — or configure each one differently. **Legend (default theme):** 🔵 blue = **primary** button · ⚫ black = **secondary** button ### Prechat form The prechat form uses a **primary** button for its main call to action, **Start chat** (🔵). ![Prechat form with the primary Start chat button](../images/webchat-theming-prechat.png) ### Chat window In the chat window you can see both button types. Quick-reply options (here, **Yes / No** ⚫) and the **Cancel** action (⚫) are **secondary** buttons, while the message **send** action and **Ok** (🔵) are **primary** buttons. ![Chat window with secondary quick-reply buttons and the primary send button](../images/webchat-theming-window.png) ![End conversation dialog with primary Ok and secondary Cancel buttons](../images/webchat-theming-end-chat.png) ## Button properties Below, is the table of configuration that you can use to change the button colours, the chat theme property is what is used to change the colour. | Chat Theme Property | Example value | Description | | --- | --- | --- | | buttonPrimaryTextColor | `#FFFFFF` | Changes the primary button text colour | | buttonPrimaryTextColorDisabled | `#888888` | Changes the primary button text colour when it's disabled | | buttonPrimaryBackgroundColor | `#000080` | Changes the primary button background colour | | buttonPrimaryBackgroundColorHover | `#00008B` | Changes the primary button background colour when it's hover | | buttonPrimaryBackgroundColorActive | `#000099` | Changes the primary button background colour when it's active | | buttonPrimaryBackgroundColorDisabled | `#000066` | Changes the primary button background colour when it's disabled | | buttonSecondaryTextColor | `#FFFFFF` | Changes the secondary button text colour | | buttonSecondaryTextColorDisabled | `#888888` | Changes the secondary button text colour when it's disabled | | buttonSecondaryBackgroundColor | `#ADD8E6` | Changes the secondary button background colour | | buttonSecondaryBackgroundColorHover | `#B0E0E6` | Changes the secondary button background colour when it's hover | | buttonSecondaryBackgroundColorActive | `#87CEEB` | Changes the secondary button background colour when it's active | | buttonSecondaryBackgroundColorDisabled | `#B0C4DE` | Changes the secondary button background colour when it's disabled | | buttonBorderRadius | `0px` | Changes the button rounding | To apply a theme, add a `themeCustom` object to the `config` block of your chat script, using the properties from the table above. The full script then looks like this — note the `themeCustom` object nested inside `config`: ```html ``` ### Images with buttons changed Once applied, the theme properties above change the primary and secondary button colours across the widget. In the examples below the header is set to blue, the **primary** buttons (send, **Ok**) use navy blue, and the **secondary** buttons (**Cancel**) use light blue. ![Chat window with the custom theme applied](../images/webchat-theming-window-custom-theme.png) ![End conversation dialog with the custom theme applied](../images/webchat-theming-end-chat-custom-theme.png) --- ## Authentication :::warning BETA **The Contact Center Campaigns API is currently in Beta.** See the [Overview](./overview.md) for details. ::: ## Overview The Contact Center Campaigns API uses two request headers to authenticate and route every request: - **`X-API-Key`** — an Admin Console API Key belonging to an app that has the **Contact Center Campaigns** API Product attached - **`X-8x8-Tenant`** — your 8x8 tenant name Unlike the legacy [Contact Center Dynamic Campaigns API](/actions-events/docs/cc-managing-campaign-status), HTTP Basic authentication is not supported. ## Admin Console API Key Admin Console API Keys are managed centrally through the 8x8 Admin Console. Every key belongs to an *app*, and each app has one or more *API Products* attached to it — the Product controls which APIs the key can be used against. The Contact Center Campaigns API requires the **Contact Center Campaigns** API Product. ### How to obtain a key If the API Keys option isn't visible in Admin Console, your account doesn't have the required permission. See the [required permission](#required-permission) section below. ![Admin Console API Keys Location](../../../analytics/images/API_Key_Generation.png) 1. Log into **[8x8 Admin Console](https://admin.8x8.com)** 2. In the **SETUP** section, click **API Keys** ![API Keys Dashboard](../../../analytics/images/API_Key_List.png) 3. Click **Create App** (or edit an existing app if you already have one you want to use) 4. Enter an application name (no spaces allowed) 5. In the **API Products** dropdown, select **Contact Center Campaigns** ![Create App Dialog](../../images/admin-console-campaigns-create-app.png) 6. Click **Save** to generate your key The dashboard will display your newly created app with the generated API key. The key always starts with `eght_` and can be viewed at any time by clicking the eye icon. ![Generated API Key](../../images/admin-console-campaigns-dashboard.png) ### Required permission Creating or managing Admin Console API Keys requires the **Application Credentials** permission. This is granted by the **Company Admin** role, or can be granted via a custom role. For full details on API Key management, see [How to get API Keys](../../../analytics/docs/how-to-get-api-keys). ### Key format - Admin Console API Keys always start with `eght_` - The key is passed in the `X-API-Key` HTTP header ## Tenant identification Every request must include an `X-8x8-Tenant` header identifying your tenant: ```text X-8x8-Tenant: your-tenant-name ``` The tenant name is distinct from the `{customer-site}` value that appears in the URL path. The customer site identifies a region (`US1`, `US2`, `UK3`); the tenant name identifies your specific tenant within that region. ## Combined header usage Every request with a body sends all three headers together: ```text X-API-Key: eght_your_admin_console_key X-8x8-Tenant: your-tenant-name Content-Type: application/vnd.campaigns.v1+json ``` ## Troubleshooting authentication **401 Unauthorized** - The `X-API-Key` header is missing or malformed - The key doesn't start with `eght_` - The key has been revoked or the app has been deleted in Admin Console **403 Forbidden** - The key is valid, but the **Contact Center Campaigns** API Product is not attached to the app. Edit the app in Admin Console and add the Product. - The Admin Console role granting the key doesn't permit this API **404 Not Found (on every request)** - The `X-8x8-Tenant` header value doesn't match a known tenant, or is missing - The `{customer-site}` segment of the URL is wrong — `US1`, `US2`, and `UK3` are the only valid values For the full error catalogue, see [Troubleshooting](./troubleshooting.md). ## Next steps - [Endpoints](./endpoints.md) - Request and response reference - [Campaign State Machine](./state-machine.md) - Which actions are valid from which states - [Troubleshooting](./troubleshooting.md) - Common issues and debugging guide --- ## Add Records :::warning BETA **The Contact Center Campaigns API is currently in Beta.** See the [Overview](../overview.md) for details. ::: ```text POST https://api.8x8.com/cc/{customer-site}/campaigns/v1/{campaignId}/records ``` Adds 1-100 records to a campaign's call list in a single request. The campaign **must** be dynamic to add records — see [Campaign state requirements](#campaign-state-requirements) below. For request conventions (required headers, media type, `{customer-site}` values) that apply to all endpoints, see [Endpoints](../endpoints.md#common-request-conventions). ## Path parameters | Name | Type | Required | Description | |--------------|------|----------|---------------------------------------------------------------| | `campaignId` | UUID | ✓ | Identifier of the campaign to add records to. While there is no GET endpoint, the campaign ID can be retrieved from the network tab of your browser when viewing the **Campaign List** page in Configuration Manager. | ## Request body | Field | Type | Required | Description | |-----------|-------|----------|----------------------------------------------------------------------| | `records` | array | ✓ | Array of 1-100 record objects. See below for per-item fields. | Each item in `records` is an `AddRecordRequest`: | Field | Type | Required | Constraints | Description | |---------------|-----------|----------|------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------| | `crmRecordId` | string | ✓ | 1-32 characters; unique in campaign | CRM record identifier. This can be found on the customer record in Agent Workspace. | | `scheduleAt` | timestamp | no | ISO 8601 (e.g. `2026-04-23T14:00:00Z`) | Earliest time the record may be attempted. Records are ordered by earliest `scheduleAt` first. | | `priority` | enum | no | default `HIGH` | Dialing priority — `HIGH`, `MEDIUM`, or `LOW`. See [`RecordPriority`](../field-reference.md#recordpriority). | | `rank` | number | no | auto-assigned if omitted | Ordering within the priority group. If omitted, the server assigns a rank equal to the current number of records in that priority group plus one. | ## All-or-nothing semantics Record addition is atomic — the entire request either succeeds or fails. The server rejects the whole request (with no records added) if any of the following are true: - More than 100 records are supplied - Two or more records in the request body share the same `crmRecordId` - Any supplied `crmRecordId` already exists on the campaign - Any record fails validation (blank `crmRecordId`, `crmRecordId` longer than 32 characters, invalid `scheduleAt`, etc.) There is no partial-success mode. On failure, no records from the batch are persisted. ## Campaign state requirements Records can only be added when the campaign is **dynamic** and is in a state that allows record edits. Adding records to a non-dynamic campaign returns `400 Bad Request` with an "Invalid Operation" message indicating that `isDynamic` on the campaign is `false`. Records also **cannot** be added when the campaign is: - `BUILDING` or `STARTING` (transient build/start states) - `COMPLETE` (finished) - `DELETED` (soft-deleted) For the full list of campaign states, see the [State Machine](../state-machine.md). ## Response Returns `200 OK` with an `AddRecordsResponse` containing the server-side representation of each successfully added record. Each record now has a server-assigned `id`, a `state` of `PENDING`, a `nextAttemptAfter` timestamp (set to the supplied `scheduleAt` if one was provided, otherwise to the creation time), and a `type` reflecting how the campaign is configured (typically `DYNAMIC` for records added through this API). ## Status codes | Code | Meaning | |------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 200 | Records added successfully; response body contains the added records | | 400 | Validation failure — `records` empty, more than 100 items, `crmRecordId` blank or longer than 32 characters, duplicate `crmRecordId` within the request, campaign is not dynamic, etc. | | 401 | Missing or invalid `X-API-Key` | | 403 | API Key valid but the **Contact Center Campaigns** API Product is not attached | | 404 | Campaign not found for the tenant | | 409 | Campaign is in a state that rejects record additions (e.g. `BUILDING`, `COMPLETE`), or one or more `crmRecordId` values already exist in the campaign | ## Examples **Single record:** ```bash curl --request POST \ 'https://api.8x8.com/cc/US1/campaigns/v1/123e4567-e89b-12d3-a456-426614174000/records' \ --header 'X-API-Key: eght_your_admin_console_key' \ --header 'X-8x8-Tenant: my-tenant' \ --header 'Content-Type: application/vnd.campaigns.v1+json' \ --data '{ "records": [ { "crmRecordId": "CRM-12345", "priority": "HIGH" } ] }' ``` **Multiple records with mixed priorities and schedules:** ```bash curl --request POST \ 'https://api.8x8.com/cc/US1/campaigns/v1/123e4567-e89b-12d3-a456-426614174000/records' \ --header 'X-API-Key: eght_your_admin_console_key' \ --header 'X-8x8-Tenant: my-tenant' \ --header 'Content-Type: application/vnd.campaigns.v1+json' \ --data '{ "records": [ { "crmRecordId": "CRM-12345", "priority": "HIGH", "rank": 1.0 }, { "crmRecordId": "CRM-12346", "priority": "MEDIUM", "scheduleAt": "2026-04-23T14:00:00Z" }, { "crmRecordId": "CRM-12347", "priority": "LOW" } ] }' ``` **Example success response:** ```json { "records": [ { "id": "789e4567-e89b-12d3-a456-426614174000", "type": "DYNAMIC", "crmRecordId": "CRM-12345", "priority": "HIGH", "rank": 1.0, "nextAttemptAfter": "2026-04-22T11:00:00Z", "retryCount": 0, "state": "PENDING" }, { "id": "789e4567-e89b-12d3-a456-426614174001", "type": "DYNAMIC", "crmRecordId": "CRM-12346", "priority": "MEDIUM", "rank": 2.0, "nextAttemptAfter": "2026-04-23T14:00:00Z", "retryCount": 0, "state": "PENDING" }, { "id": "789e4567-e89b-12d3-a456-426614174002", "type": "DYNAMIC", "crmRecordId": "CRM-12347", "priority": "LOW", "rank": 3.0, "nextAttemptAfter": "2026-04-22T11:00:00Z", "retryCount": 0, "state": "PENDING" } ] } ``` ## Next steps - [Modify Campaign](./modify-campaign.md) - `PATCH /campaigns/{campaignId}` - [Campaign State Machine](../state-machine.md) - When records can be added - [Field Reference](../field-reference.md) - Full schema for every object and enum - [Troubleshooting](../troubleshooting.md) - Debugging guide --- ## Modify Campaign :::warning BETA **The Contact Center Campaigns API is currently in Beta.** See the [Overview](../overview.md) for details. ::: ```text PATCH https://api.8x8.com/cc/{customer-site}/campaigns/v1/{campaignId} ``` Transitions a campaign through its [state machine](../state-machine.md) and optionally toggles its `enabled` flag. For request conventions (required headers, media type, `{customer-site}` values) that apply to all endpoints, see [Endpoints](../endpoints.md#common-request-conventions). ## Path parameters | Name | Type | Required | Description | |--------------|------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `campaignId` | UUID | ✓ | Identifier of the campaign to modify. While there is no GET endpoint, the campaign ID can be retrieved from the network tab of your browser when viewing the **New Campaigns** page in Configuration Manager. | ## Request body | Field | Type | Required | Description | |-----------|---------|----------|----------------------------------------------------------------------------------------------------------------------------| | `action` | enum | ✓ | The action to perform — one of `BUILD`, `RESET`, `START`, `PAUSE`, `RESUME`, `RETRY`, `CANCEL`, `PURGE`. See [`CampaignAction`](../field-reference.md#campaignaction). | | `enabled` | boolean | no | Set to `true` or `false` to enable or disable the campaign. Omit to leave the current value unchanged. | Each action is only valid from certain states. For the complete matrix, see the [Campaign State Machine](../state-machine.md). ### `START` variant — `buildOnStart` When `action` is `START`, the request body may also include a `buildOnStart` flag: | Field | Type | Required | Description | |----------------|---------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `buildOnStart` | boolean | no | When `true`, the server performs a build-then-start sequence in a single request, avoiding the need for a separate `BUILD` call first. Overrides the campaign's configured "build on start" value. When omitted or `false`, the campaign must already be in `READY`. | ## Response Returns `200 OK` with a [`Campaign`](../field-reference.md#campaign) representing the campaign's current state *after* the action was accepted. For transient states (`BUILDING`, `STARTING`), the response reflects the in-progress state; the campaign settles into its target state asynchronously. ## Status codes | Code | Meaning | |------|-----------------------------------------------------------------------------------------------------------------| | 200 | Action accepted; response body contains the current campaign | | 400 | Request body is malformed or fails validation — see [Troubleshooting](../troubleshooting.md#400-bad-request) | | 401 | Missing or invalid `X-API-Key` — see [Authentication](../authentication.mdx#troubleshooting-authentication) | | 403 | API Key valid but the **Contact Center Campaigns** API Product is not attached | | 404 | Campaign not found for the tenant, or the `{customer-site}` segment is wrong | | 409 | Action is not valid for the campaign's current state — see the [State Machine](../state-machine.md) | ## Examples **Simple action — `BUILD`:** ```bash curl --request PATCH \ 'https://api.8x8.com/cc/US1/campaigns/v1/123e4567-e89b-12d3-a456-426614174000' \ --header 'X-API-Key: eght_your_admin_console_key' \ --header 'X-8x8-Tenant: my-tenant' \ --header 'Content-Type: application/vnd.campaigns.v1+json' \ --data '{"action":"BUILD"}' ``` **Toggle `enabled`:** ```bash curl --request PATCH \ 'https://api.8x8.com/cc/US1/campaigns/v1/123e4567-e89b-12d3-a456-426614174000' \ --header 'X-API-Key: eght_your_admin_console_key' \ --header 'X-8x8-Tenant: my-tenant' \ --header 'Content-Type: application/vnd.campaigns.v1+json' \ --data '{"action":"BUILD","enabled":true}' ``` **Combined build-and-start:** ```bash curl --request PATCH \ 'https://api.8x8.com/cc/US1/campaigns/v1/123e4567-e89b-12d3-a456-426614174000' \ --header 'X-API-Key: eght_your_admin_console_key' \ --header 'X-8x8-Tenant: my-tenant' \ --header 'Content-Type: application/vnd.campaigns.v1+json' \ --data '{"action":"START","buildOnStart":true}' ``` **Example success response:** ```json { "id": "123e4567-e89b-12d3-a456-426614174000", "tenantId": "my-tenant", "integrationType": "contactual", "startTime": "2026-04-22T10:00:00Z", "endTime": "2026-04-22T18:00:00Z", "state": "RUNNING", "displayStatus": "RUNNING", "recordCount": 1500, "recordCounts": [ { "type": "DYNAMIC", "state": "PENDING", "count": 1455 }, { "type": "DYNAMIC", "state": "COMPLETE", "result": "SUCCESS", "count": 45 } ], "lastBuildTime": "2026-04-22T09:55:00Z", "startedTime": "2026-04-22T10:00:05Z" } ``` Fields omitted from the response (for example `completedTime` for a running campaign) are absent from the JSON rather than present with a `null` value. ## Next steps - [Add Records](./add-records.md) - `POST /campaigns/{campaignId}/records` - [Campaign State Machine](../state-machine.md) - Valid action and state matrix - [Field Reference](../field-reference.md) - Full schema for every object and enum - [Troubleshooting](../troubleshooting.md) - Debugging guide --- ## Endpoints :::warning BETA **The Contact Center Campaigns API is currently in Beta.** See the [Overview](./overview.md) for details. ::: The Contact Center Campaigns API exposes two endpoints: | Endpoint | Purpose | |-----------------------------------------------------------------------------------|------------------------------------------------------------| | [`PATCH /campaigns/{campaignId}`](./endpoints/modify-campaign.md) | Transition campaign state and toggle the `enabled` flag | | [`POST /campaigns/{campaignId}/records`](./endpoints/add-records.md) | Add 1-100 records to a campaign's work list | ## Common request conventions All endpoints are rooted at: ```text https://api.8x8.com/cc/{customer-site}/campaigns/v1 ``` | Convention | Value | |---------------------------|------------------------------------------------------------------------------------------------------------------------------| | `{customer-site}` segment | `US1`, `US2`, or `UK3` — see [Getting Started](./getting-started.mdx#base-url) | | Authentication | `X-API-Key` header — see [Authentication](./authentication.mdx) | | Tenant header | `X-8x8-Tenant` — required on every request | | Request body media type | `Content-Type: application/vnd.campaigns.v1+json` — required on any request with a body | | Identifiers | Server-generated identifiers (`id`, `campaignId`) are UUIDs. `crmRecordId` is a customer-supplied string (1-32 characters). | | Timestamps | ISO 8601 with offset (e.g. `2026-04-22T10:00:00Z`) | ## Next steps - [Modify Campaign](./endpoints/modify-campaign.md) - `PATCH /campaigns/{campaignId}` - [Add Records](./endpoints/add-records.md) - `POST /campaigns/{campaignId}/records` - [Campaign State Machine](./state-machine.md) - When each action is valid - [Field Reference](./field-reference.md) - Full schema for every object and enum - [Troubleshooting](./troubleshooting.md) - Debugging guide --- ## Field Reference :::warning BETA **The Contact Center Campaigns API is currently in Beta.** See the [Overview](./overview.md) for details. ::: This page documents the full schema for every object and enum used in the Contact Center Campaigns API. ## Objects ### `Campaign` Returned in the response body of every `PATCH /campaigns/{campaignId}` call. | Field | Type | Description | Example | |--------------------|--------------------------|--------------------------------------------------------------------|------------------------------------------| | `id` | UUID | Campaign unique identifier | `123e4567-e89b-12d3-a456-426614174000` | | `tenantId` | string | Tenant identifier | `tenant-123` | | `integrationType` | string | Integration type | `contactual` | | `startTime` | timestamp | Scheduled start time (ISO 8601) | `2026-04-22T10:00:00Z` | | `endTime` | timestamp | Scheduled end time (ISO 8601) | `2026-04-22T18:00:00Z` | | `state` | [`CampaignState`](#campaignstate) | Current campaign state | `RUNNING` | | `displayStatus` | [`CampaignDisplayStatus`](#campaigndisplaystatus) | UI-oriented derived status | `RUNNING` | | `recordCount` | number | Total number of records | `1500` | | `recordCounts` | array of [`RecordCount`](#recordcount) | Breakdown of record counts by type, state, and result | see example | | `lastBuildTime` | timestamp | Last time campaign records were built (ISO 8601) | `2026-04-22T09:55:00Z` | | `lastPurgedTime` | timestamp | Last time campaign was purged (ISO 8601) | `2026-04-21T23:00:00Z` | | `startedTime` | timestamp | Time campaign actually started (ISO 8601) | `2026-04-22T10:00:05Z` | | `completedTime` | timestamp | Time campaign completed (ISO 8601) | `2026-04-22T17:45:00Z` | Fields whose value is unknown or not applicable are omitted from the response rather than included with a `null` value. #### `RecordCount` A single entry in `Campaign.recordCounts`, aggregating record counts by the combination of type, state, and result. | Field | Type | Description | Example | |----------|-----------------------------------------|-----------------------------|-----------| | `type` | [`RecordType`](#recordtype) | Record type | `DYNAMIC` | | `state` | [`RecordState`](#recordstate) | Record state | `READY` | | `result` | [`RecordResult`](#recordresult) | Record result | `SUCCESS` | | `count` | number | Count of matching records | `45` | ### `Record` Returned as part of the response to `POST /campaigns/{campaignId}/records`. | Field | Type | Description | Example | |--------------------|-----------------------------------|--------------------------------------------------------|------------------------------------------| | `id` | UUID | Record unique identifier | `789e4567-e89b-12d3-a456-426614174000` | | `type` | [`RecordType`](#recordtype) | Record type | `DYNAMIC` | | `crmRecordId` | string | CRM record identifier (1-32 characters) | `CRM-12345` | | `priority` | [`RecordPriority`](#recordpriority) | Priority for dialing order | `HIGH` | | `rank` | number | Ranking within priority group | `1.5` | | `nextAttemptAfter` | timestamp | Earliest time the record may be attempted next | `2026-04-23T11:30:00Z` | | `retryCount` | number | Number of retry attempts so far | `2` | | `state` | [`RecordState`](#recordstate) | Current record state | `PENDING` | | `stateReason` | string | Reason for the current state (if any) | `Scheduled for retry` | | `result` | [`RecordResult`](#recordresult) | Final result of record processing, once complete | `SUCCESS` | ### `AddRecordRequest` Sent as each item inside the `records` array on `POST /campaigns/{campaignId}/records`. | Field | Type | Required | Constraints | Description | |---------------|-----------------------------------|----------|--------------------------------------|------------------------------------------------------------------------------------------------| | `crmRecordId` | string | ✓ | 1-32 characters; unique in campaign | CRM record identifier | | `scheduleAt` | timestamp | no | ISO 8601 | Earliest time the record may be attempted. Records are ordered by earliest `scheduleAt` first. | | `priority` | [`RecordPriority`](#recordpriority) | no | default `HIGH` | Dialing priority | | `rank` | number | no | auto-assigned if omitted | Ordering within the priority group. If omitted, the server assigns a rank equal to the current number of records in that priority group plus one. | ## Enums ### `CampaignState` The 11 possible states of a campaign. Used in `Campaign.state` and as the precondition for every action. | Value | Description | |----------------|-----------------------------------------------------------------------------------------------------------------------------------| | `CREATED` | The campaign has just been created and is not yet active. You can edit or delete the campaign in this state. | | `BUILDING` | The system is querying CRM data and preparing the campaign for use. The CRM query and filter can be edited and the campaign rebuilt as many times as needed before it is started. | | `READY` | The campaign is fully built and ready to be started. You can still edit or delete the campaign at this stage. | | `PENDING` | The campaign has been started and is waiting for its scheduled start time. No further edits are allowed. | | `STARTING` | The campaign is in the process of starting, which may include final preparations before it becomes active. | | `RUNNING` | The campaign is currently active and dialing records as agents become available. No edits or deletions are allowed. | | `PAUSED` | New records are no longer being added to the dialing queue, but records already queued will still be dialed. The campaign can be resumed. | | `COMPLETE` | The campaign has finished running, either because it reached its end time or was cancelled. A completed campaign cannot be restarted, but it can be deleted. | | `BUILD_ERROR` | An error occurred while building the campaign. You can edit or delete the campaign to resolve the issue. | | `RUN_ERROR` | An error occurred while running the campaign. The campaign cannot be edited or deleted until the error is resolved. | | `DELETED` | The campaign has been removed from active use. It is retained for record-keeping but cannot be modified or restored. | See the [State Machine](./state-machine.md) for transitions. ### `CampaignAction` The 8 actions that can be sent as the `action` field of a `PATCH /campaigns/{campaignId}` request. | Value | Description | |----------|-----------------------------------------------------------------------------------------------------------| | `BUILD` | Prepares a newly created campaign for use. | | `RESET` | Resets a campaign that is `READY` or has a `BUILD_ERROR`, allowing you to rebuild it. | | `START` | Starts a campaign that is `READY`, moving it to the next phase. | | `PAUSE` | Temporarily stops a `RUNNING` campaign. Can be resumed later. | | `RESUME` | Resumes a `PAUSED` campaign, returning it to `RUNNING`. | | `RETRY` | Retries a campaign that encountered a `RUN_ERROR`. | | `CANCEL` | Cancels a campaign that is `PENDING`, `RUNNING`, `PAUSED`, or in `RUN_ERROR`. | | `PURGE` | Clears all queued interactions for a campaign that is `PAUSED`, `RUN_ERROR`, or `COMPLETE`. | For the full valid-from-states matrix, see the [State Machine](./state-machine.md#actions-and-their-preconditions). ### `CampaignDisplayStatus` A UI-oriented derived status attached to every `Campaign`. Each value is derived from the campaign's `state` and `enabled` flag. Use `state` for automation logic; `displayStatus` is only intended for human-readable display. | Value | Derived from | |-----------------|------------------------------------------------------------------------------------------| | `NEW` | `state = CREATED` | | `BUILDING` | `state = BUILDING` or `state = STARTING` | | `BUILD_FAILED` | `state = BUILD_ERROR` | | `READY_TO_RUN` | `state = READY` | | `SCHEDULED` | `state = PENDING` | | `RUNNING` | `state = RUNNING` | | `PAUSED` | `state = PAUSED` with no pending purge | | `PURGED` | `state = PAUSED` and the campaign has been purged | | `ERROR` | `state = RUN_ERROR` with no pending purge | | `ERROR_PURGED` | `state = RUN_ERROR` and the campaign has been purged | | `COMPLETED` | `state = COMPLETE` or `state = DELETED` | | `STOPPED` | `state = COMPLETE` or `state = DELETED` when the campaign was cancelled via `CANCEL` | | `DISABLED` | `enabled = false` (regardless of state) | ### `RecordType` | Value | Description | |-------------|-----------------------------------------------------------| | `CRM_QUERY` | Record derived from a CRM query configured on the campaign. | | `DYNAMIC` | Record added dynamically (e.g. through this API). | ### `RecordState` The 9 possible states of a record. | Value | Description | |--------------------|-----------------------------------------------------------------------------------------------| | `PENDING` | Waiting to be processed. | | `REQUESTED` | An interaction has been requested. | | `QUEUED` | An interaction is queued for routing. | | `CANCEL_REQUESTED` | Cancellation is in progress. | | `AGENT_ACCEPTED` | An agent has accepted the interaction. | | `COMPLETE` | Processing completed successfully. | | `REJECTED` | Record was rejected as not valid (for example, no valid phone numbers). | | `DELETED` | Record has been soft-deleted. | | `ERROR` | An error occurred during processing. | ### `RecordResult` The final outcome of a record's processing, set once the record reaches a terminal state. | Value | Description | |------------------------|-------------------------------------------------------------------------------------| | `SUCCESS` | Completed with a successful interaction. | | `NO_VALID_NUMBER` | Rejected — no valid phone numbers were found in CRM. | | `SCHEDULE_COMPLETE` | Schedule has no future active date. | | `INTERACTION_FAILED` | The interaction was not successful and no retries are configured. | | `MAX_ATTEMPTS_REACHED` | The interaction was not successful after the configured number of retries. | | `INTERACTION_SKIPPED` | The interaction was skipped by the agent. | | `CALLBACK_SCHEDULED` | A callback was scheduled by the agent. | ### `RecordPriority` Dialing priority. Records are dialed `HIGH` first, then `MEDIUM`, then `LOW`. Within a priority group, records are ordered by `rank`. | Value | Description | |----------|----------------| | `HIGH` | Highest priority (default) | | `MEDIUM` | Medium priority | | `LOW` | Lowest priority | ## Next steps - [Endpoints](./endpoints.md) - Request and response reference - [Campaign State Machine](./state-machine.md) - Valid action / state matrix - [Troubleshooting](./troubleshooting.md) - Error format and common issues --- ## Getting Started :::warning BETA **The Contact Center Campaigns API is currently in Beta.** See the [Overview](./overview.md) for details. ::: This guide walks through a minimal end-to-end flow: build a campaign, start it, add a record, and pause it. Each step links to the endpoint reference for full request and response detail. ## Prerequisites You will need: 1. **Your 8x8 tenant name** (not the customer site — see below) 2. **An Admin Console API Key** with the **Contact Center Campaigns** API Product attached to its app — see [Authentication](./authentication.mdx) 3. **An existing dynamic campaign** — create and configure the campaign in Contact Center Configuration Manager first, and note its **campaign ID** 4. **Your customer site identifier** — one of `US1`, `US2`, or `UK3`, based on your tenant configuration ## Base URL All requests use the following pattern: ```text https://api.8x8.com/cc/{customer-site}/campaigns/v1/{campaignId} ``` Where `{customer-site}` is your tenant's customer site: | Customer site | Region | |---------------|---------| | `US1` | US East | | `US2` | US West | | `UK3` | UK | > 📘 **Customer site vs tenant name** > > The `{customer-site}` value identifies the *region* your tenant is deployed in and appears in the URL path. Your *tenant name* is a separate value sent in the `X-8x8-Tenant` request header. If you're unsure of either value, contact 8x8 support. ## Required headers Every request must include: ```text X-API-Key: eght_your_admin_console_key X-8x8-Tenant: your-tenant-name ``` Any request with a body must additionally include: ```text Content-Type: application/vnd.campaigns.v1+json ``` ## Quick start ### Step 1 — Build the campaign Call [`PATCH /campaigns/{campaignId}`](./endpoints/modify-campaign.md) with `action: "BUILD"`. The campaign moves `CREATED → BUILDING → READY`. `BUILDING` is transient — wait a few seconds before the next step (future API enhancements will have an API to poll, to confirm when the Campaign has finished building). > 📘 **Build-and-start in one call** > > Steps 1 and 2 can be collapsed into a single request by sending `action: "START"` with [`buildOnStart: true`](./endpoints/modify-campaign.md#start-variant--buildonstart) from `CREATED`. ### Step 2 — Start the campaign Once the campaign is in `READY`, call [`PATCH /campaigns/{campaignId}`](./endpoints/modify-campaign.md) with `action: "START"`. The campaign moves to `RUNNING` — or to `PENDING` first if a future `startTime` is configured, transitioning to `RUNNING` when its scheduled window opens. ### Step 3 — Add records Call [`POST /campaigns/{campaignId}/records`](./endpoints/add-records.md) with a `records` array of 1-100 entries. Each record needs at minimum a `crmRecordId` (your identifier, unique within the campaign); `priority`, `scheduleAt`, and `rank` are optional. Record addition is **atomic** — if any record in the batch is invalid or its `crmRecordId` collides with an existing one, the whole request is rejected and nothing is persisted. ### Step 4 — Pause the campaign Call [`PATCH /campaigns/{campaignId}`](./endpoints/modify-campaign.md) with `action: "PAUSE"`. Resume with `"RESUME"`, or end the campaign early with `"CANCEL"`. ## Next steps - **[Authentication](./authentication.mdx)** - Get an Admin Console API Key - **[Modify Campaign endpoint](./endpoints/modify-campaign.md)** - Full request and response reference, including the `buildOnStart` shortcut for combining steps 1 and 2 - **[Add Records endpoint](./endpoints/add-records.md)** - Batch record addition reference - **[Campaign State Machine](./state-machine.md)** - Which actions are valid from which states - **[Field Reference](./field-reference.md)** - Full schema for campaigns and records - **[Troubleshooting](./troubleshooting.md)** - Error format and common issues ## Need help? 1. Check the [Troubleshooting](./troubleshooting.md) guide 2. Confirm the Admin Console API Key has the **Contact Center Campaigns** API Product attached 3. Contact 8x8 support for credentials or access issues --- ## Overview :::warning BETA **The Contact Center Campaigns API is currently in Beta.** ::: The 8x8 Contact Center Campaigns API is a REST API for programmatically controlling the lifecycle of campaigns and for adding records to dynamic campaigns. ## What is the Contact Center Campaigns API? The API covers two responsibilities: - **Campaign lifecycle control** — transition a campaign through its state machine using explicit actions (`BUILD`, `RESET`, `START`, `PAUSE`, `RESUME`, `RETRY`, `CANCEL`, `PURGE`). - **Record management** — add 1 to 100 records at a time to a dynamic campaign's call list, each with an optional schedule, priority, and rank. Campaign *creation* and *configuration* still happen in Contact Center Configuration Manager. This API controls lifecycle and records for campaigns that already exist. ## When to use this API Use the Contact Center Campaigns API when you need to: - Drive campaign lifecycle programmatically from an external system - Add records in batches of up to 100 in a single request ## Relationship to the legacy Contact Center Dynamic Campaigns API This API is part of an entirely new campaigns service. It does not share data with the legacy [Contact Center Dynamic Campaigns API](/actions-events/docs/cc-managing-campaign-status) — the two APIs manage completely separate campaigns. - Campaigns belonging to the new service appear in the **New Campaigns** section of Configuration Manager. They are created, configured, and managed there — and programmatically via this API. There is no crossover in either direction. Changes made through this API are only reflected in the **New Campaigns** section of Configuration Manager and are invisible to the legacy Campaign Manager; the reverse is also true. > ✅ **Recommendation for beta customers** > > If you are participating in the beta, create new campaigns in the new service (through the **New Campaigns** section of Configuration Manager) rather than in the legacy Campaign Manager. The legacy API remains available only for customers who already have legacy campaigns to manage. The two APIs differ in a number of ways: | Aspect | Legacy Dynamic Campaigns API | Contact Center Campaigns API (BETA) | |---------------------|-------------------------------------------------------------------------|-----------------------------------------------------------------| | Campaigns managed | Legacy Campaign Manager | New Campaigns section of Configuration Manager | | Authentication | HTTP Basic with Data/Action Request Tokens | `X-API-Key` with an Admin Console API Key | | Base URL | `https://vcc-{ccPlatform}.8x8.com/api/tstats/campaigns/` | `https://api.8x8.com/cc/{customer-site}/campaigns/v1/` | | Record batch size | Single record per request | 1-100 records per request | ## Getting started To start using the API: 1. **[Set up authentication](./authentication.mdx)** — create an Admin Console API Key and attach the **Contact Center Campaigns** API Product 2. **[Work through the Getting Started guide](./getting-started.mdx)** — an end-to-end walkthrough using curl 3. **[Review the endpoints reference](./endpoints.md)** — full request and response detail ## Next steps - [Getting Started](./getting-started.mdx) - End-to-end quick start - [Authentication](./authentication.mdx) - Admin Console API Key setup - [Endpoints](./endpoints.md) - Full endpoint reference - [Campaign State Machine](./state-machine.md) - States, actions, and transitions - [Field Reference](./field-reference.md) - Object schemas and enum values - [Troubleshooting](./troubleshooting.md) - Error format and common issues --- ## Campaign State Machine :::warning BETA **The Contact Center Campaigns API is currently in Beta.** See the [Overview](./overview.md) for details. ::: Every `PATCH /campaigns/{campaignId}` request supplies an `action`. The server validates that action against the campaign's current `state` and responds with `409 Conflict` when the action isn't allowed. Understanding the state machine is a prerequisite to using this API reliably. ## States A campaign is always in exactly one state. The full set of states: | State | Description | Transient? | |---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------| | `CREATED` | The campaign has just been created and is not yet active. | no | | `BUILDING` | The system is querying CRM data and preparing the campaign for use. The CRM query and filter can be edited and the campaign rebuilt as many times as needed before it is started. | yes | | `READY` | The campaign is fully built and ready to be started. | no | | `PENDING` | The campaign has been started and is waiting for its scheduled start time. | no | | `STARTING` | The campaign is being started, including any final preparations before it becomes active. | yes | | `RUNNING` | The campaign is currently active and dialing records when agents are available. | no | | `PAUSED` | The campaign is paused, new records are no longer being added to the dialing queue, but records already queued will still be dialed. The campaign can be resumed. | no | | `COMPLETE` | The campaign has finished running, either because it reached its end time or was cancelled. A completed campaign cannot be restarted. | no | | `BUILD_ERROR` | An error occurred while building the campaign. | no | | `RUN_ERROR` | An error occurred while running the campaign. | no | | `DELETED` | The campaign has been soft-deleted and is retained only for record-keeping. | no | Transient states (`BUILDING`, `STARTING`) are the result of an action the server is still processing. The campaign will leave them without further client input. ## Actions and their preconditions Each action is valid only from a specific set of states: | Action | Valid from states | Result | |----------|--------------------------------------------------|-------------------------------------------------------------------------------------| | `BUILD` | `CREATED`, `READY`, `BUILD_ERROR` | Campaign moves to `BUILDING`, then settles in `READY` (or `BUILD_ERROR` on failure) | | `RESET` | `READY`, `BUILD_ERROR` | Campaign returns to `CREATED`, allowing it to be rebuilt | | `START` | `READY` | Campaign moves to `PENDING` or `STARTING`, then to `RUNNING` | | `PAUSE` | `RUNNING` | Campaign moves to `PAUSED` | | `RESUME` | `PAUSED` | Campaign returns to `RUNNING` | | `RETRY` | `RUN_ERROR` | Campaign resumes processing | | `CANCEL` | `PENDING`, `RUNNING`, `PAUSED`, `RUN_ERROR` | Campaign moves to `COMPLETE` | | `PURGE` | `PAUSED`, `RUN_ERROR`, `COMPLETE` | Clears queued interactions for the campaign | If an action is sent from a state not listed as valid, the API returns `409 Conflict`. See [Troubleshooting](./troubleshooting.md#409-conflict) for details. ## Visual diagram The state machine is split into two phases. The **Management phase** covers campaign creation, building, and validation before the campaign has ever run. The **Processing phase** begins when `START` is sent and covers everything from there to termination. `READY` appears in both diagrams as the bridge state: from `READY` the campaign either hands off into the Processing phase via `START`, or is cancelled without ever running. ### Management phase ```mermaid flowchart LR start(( )) CREATED([CREATED]) subgraph row2[" "] direction RL BUILD_ERROR([BUILD_ERROR]) BUILDING([BUILDING]) end subgraph row3[" "] direction RL COMPLETE([COMPLETE]) PROCESSING_PHASE(["PENDING (PROCESSING PHASE)"]) READY([READY]) end start --> CREATED CREATED -- BUILD --> BUILDING BUILDING -- error --> BUILD_ERROR BUILD_ERROR -- BUILD --> BUILDING BUILD_ERROR -- RESET --> CREATED BUILDING -- success --> READY READY -- BUILD --> BUILDING READY -- RESET --> CREATED READY -- CANCEL --> COMPLETE READY -- START --> PROCESSING_PHASE classDef invisible fill:transparent,stroke:transparent,color:transparent class row2,row3 invisible ``` ### Processing phase ```mermaid stateDiagram-v2 direction LR READY --> PENDING : START PENDING --> STARTING : schedule opens PENDING --> COMPLETE : CANCEL / schedule ends STARTING --> RUNNING : success STARTING --> BUILD_ERROR : error RUNNING --> PAUSED : PAUSE PAUSED --> RUNNING : RESUME RUNNING --> RUN_ERROR : error RUN_ERROR --> RUNNING : RETRY RUNNING --> COMPLETE : CANCEL / schedule ends PAUSED --> COMPLETE : CANCEL RUN_ERROR --> COMPLETE : CANCEL COMPLETE --> DELETED : DELETE DELETED --> [*] ``` A few things worth knowing to read the diagrams: - **Uppercase labels** (`BUILD`, `START`, `PAUSE`, `CANCEL`, …) are client actions sent via `PATCH /campaigns/{campaignId}`. - **Lowercase labels** (`success`, `error`, `schedule opens`, `schedule ends`) are automatic transitions triggered by the service — the client does not send these. - `PENDING` is **skipped** if the campaign's `startTime` is in the past when `START` is sent — the campaign transitions directly into `STARTING`. - `PURGE` does not change the campaign state and is therefore not shown as an edge. It is valid from `PAUSED`, `RUN_ERROR`, and `COMPLETE`, and clears queued interactions for the campaign. ## The `enabled` flag In addition to its state, every campaign has a boolean `enabled` flag which is independent of the state machine. When a campaign is disabled, only the `BUILD` and `RESET` actions are allowed; all other actions are rejected until the campaign is re-enabled. Toggle the flag by supplying `enabled` in the PATCH body: ```json { "action": "BUILD", "enabled": true } ``` Omit `enabled` to leave the current value unchanged. ## Display status vs actual state The campaign response includes a separate `displayStatus` field (of type [`CampaignDisplayStatus`](./field-reference.md#campaigndisplaystatus)). This is a UI-oriented derived view — for example: - `SCHEDULED` is shown when `state` is `PENDING` - `PURGED` is shown when `state` is `PAUSED` and the campaign has been purged - `DISABLED` is shown when `enabled` is `false`, regardless of state Automation that drives the API should branch on `state`, not on `displayStatus`. The full mapping table is in the [Field Reference](./field-reference.md#campaigndisplaystatus). ## Next steps - [Endpoints](./endpoints.md) - Full request and response detail - [Field Reference](./field-reference.md) - Full schema for every object and enum - [Troubleshooting](./troubleshooting.md) - Error format and common issues --- ## Troubleshooting :::warning BETA **The Contact Center Campaigns API is currently in Beta.** See the [Overview](./overview.md) for details. ::: ## Error response format All errors returned by the Contact Center Campaigns API use a consistent JSON body format. **Example — a 400 validation error:** ```json { "title": "Validation failed", "status": 400, "detail": "Campaign already contains record for Id: CRM-12345", "instance": "/cc/US1/campaigns/v1/123e4567-e89b-12d3-a456-426614174000/records", "time": "2026-04-22T10:30:00Z", "errors": [ { "field": "crmRecordId", "code": "NotBlank", "message": "must not be blank" } ] } ``` ### Body fields | Field | Type | Description | |------------|-----------------------------|--------------------------------------------------------------------------| | `title` | string | Short, human-readable summary of the problem | | `status` | number | HTTP status code | | `detail` | string | Human-readable explanation specific to this occurrence | | `instance` | string | URI reference identifying the specific occurrence of the problem | | `time` | timestamp | When the error occurred (ISO 8601) | | `errors` | array of `FieldError` | Field-level validation errors (only present on validation failures) | ### `FieldError` | Field | Type | Description | Example | |-----------|--------|-----------------------------------|------------------| | `field` | string | Name of the field in error | `crmRecordId` | | `code` | string | Error code (typically a validator name) | `NotBlank` | | `message` | string | Human-readable message | `must not be blank` | When contacting 8x8 support about an error, include the `instance` URI and `time` — these uniquely identify the occurrence in server logs. ## Common errors by status code ### 400 Bad Request The request body is malformed or fails validation. Look at `errors[]` for the specific field(s) at fault. Common causes: - Malformed JSON — missing braces, trailing commas, wrong quoting - `records` array is empty, or contains more than 100 items - `crmRecordId` is blank or longer than 32 characters - `crmRecordId` values are duplicated within the same request - `action` is not one of the [allowed values](./field-reference.md#campaignaction) - `scheduleAt` is not a valid ISO 8601 timestamp ### 401 Unauthorized The request is missing valid authentication. Common causes: - `X-API-Key` header is missing - The key value is malformed — it must start with `eght_` - The key has been revoked, or the app has been deleted in Admin Console See [Authentication](./authentication.mdx#troubleshooting-authentication). ### 403 Forbidden The API key is valid but is not authorised for this API. Most common cause: - The **Contact Center Campaigns** API Product is not attached to the app in Admin Console. Edit the app and add the Product. ### 404 Not Found The campaign, tenant, or customer site could not be resolved. Common causes: - The `campaignId` UUID does not exist within the tenant - The `X-8x8-Tenant` header is missing or points at a different tenant than the one that owns the campaign - The `{customer-site}` segment of the URL is wrong — only `US1`, `US2`, and `UK3` are valid ### 409 Conflict The request is syntactically valid but conflicts with the current server state. Common causes: - **Action invalid for current state.** For example, sending `{"action":"PAUSE"}` to a campaign in `CREATED`. See the [State Machine](./state-machine.md#actions-and-their-preconditions) for the valid-from matrix. - **Duplicate `crmRecordId`.** The record already exists in the campaign from a previous request. - **Campaign state rejects record additions.** Records cannot be added while the campaign is `BUILDING`, `STARTING`, `COMPLETE`, or `DELETED`. ### 5xx Server Errors An unexpected server error. These should be rare. - Retry the request with exponential backoff - When contacting support, include the `instance` URI and `time` from the error body ## Debugging checklist When a request doesn't do what you expect, work through the following: 1. **Customer site** — is the `{customer-site}` segment correct for your tenant (`US1`, `US2`, or `UK3`)? 2. **Tenant name** — does the `X-8x8-Tenant` header value match the tenant that owns the campaign? 3. **API Product** — does the Admin Console app that issued the key have the **Contact Center Campaigns** API Product attached? 4. **Media type** — are requests with a body sending `Content-Type: application/vnd.campaigns.v1+json`? 5. **Campaign state** — is the campaign in a state that allows the action you're sending? Check the response from a recent `PATCH` or the [State Machine](./state-machine.md). 6. **Campaign enabled flag** — if the campaign is disabled, only `BUILD` and `RESET` actions are allowed. 7. **Record constraints** — is `crmRecordId` 1-32 characters, unique in the request, and not already present in the campaign? Is the batch between 1 and 100 records? ## Getting help For issues not resolved by this guide: 1. Check the [Authentication](./authentication.mdx) and [State Machine](./state-machine.md) pages 2. Confirm the [Admin Console setup](./authentication.mdx#admin-console-api-key) 3. Contact 8x8 support, including the `instance` URI and `time` from the error response body --- ## CC Manage Phone Calls ## Authentication CC Phone Call APIs leverage the credentials from the Integration >> API Token area in Configuration Manager. These APIs use Basic Authentication. The username will be the "Username" value from this screen, it is generally the tenant name Action Request Token will be the password. `Authorization :Basic encodedValue` Where encodedValue is base64encode(username:password) ![CC Request Action Token](../images/841df98-CC-Request-Action-Token.png "CC-Request-Action-Token.png") ## Place Call For Agent Make a call in the context of a specified agent ### Parameters **Method: POST** #### Headers | Name | Required | Description | Example | | ------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of **Action Request Token** | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | | Content-Type | ✓ | Set the content type to application/json | application/json | #### Path | Name | Required | Description | Example | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager. North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | version | ✓ | The API version. The current version is 1 resulting in. v1 | v1 | | tenantId | ✓ | The CC Tenant name of the tenant to perform the action on. Tenant name is generally the same as the username above. It can be located in CC Configuration Manager @ Home :: Profile :: Tenant Name | acmecorp01 | #### Body Body is JSON and includes extTransactionData, ctlUserData which are optional arrays that can have multiple elements. See XX for a full example. | Name | Required | Description | Example | | ------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------- | | agentId | ✓ | The agentId of the agent to set the status for. This can be found in CC Configuration Manager "Users" add the "Internal Id" column, or in the "General" section of the user as "Internal Id" | ag0VYyLh0YTsymdbtYIaaaa | | prefix | ✓ | Prefix for the number to dial. Example 1 to be prepended to a 10 digit US number. Note this MUST be passed but can be an empty string | 1 | | number | ✓ | The number to dial. The number will have dial plan rules applied to convert to E.164 | 6175551984 | | callerId | ✓ | The callerid number to present. Only callerid numbers that are channels will be presented. If a non channel or empty number is provided the configured callerid for the agent is presented. | 12015551212 | | queueId | ☐ | Optionally set the queue to associate the call with. Note if no queue is associated the agent will not be set to busy status. Queue Id can be located in CC Configuration Manager "Queues/Skills" "Id" column. | 598 | | dialplanId | ☐ | Optionally set the dial plan to associate the call with. This can be used to manipulate the number to dial. Dial plan Id can be located in CC Configuration Manager "Home" "Dial Plans" in the "ID" column. When not specified the agents configured dial plan is used. | 12 | | forceCall | ☐ | If an agent has one line available but is busy on the other line, or has both lines available but busy on chat, this flag can be set to true so the agent is offered the call regardless. If the agent is busy on both lines, on break, or logged out, this flag will not help as the call is rejected. Default is false | true | | extTransactionData[].name | ☐ | Name of the element to be passed. See [extTransactionData details](/actions-events/docs/cc-manage-phone-calls#exttransationdata-details) for more information | | | extTransationData[].value | ☐ | Value for the element to be passed. See [extTransactionData details](/actions-events/docs/cc-manage-phone-calls#exttransationdata-details) for more information | | | ctlUserData[].name | ☐ | Name of the element to be passed. See [ctlUserData details](https://support.8x8.com/cloud-contact-center/8x8-contact-center/developers/8x8-contact-center-click-to-dial-api#ctl_userdata) for more information | | | ctlUserData[].value | ☐ | Value for the element to be passed. See [ctlUserData details](https://support.8x8.com/cloud-contact-center/8x8-contact-center/developers/8x8-contact-center-click-to-dial-api#ctl_userdata) for more information | | [Phone Call API Reference](/actions-events/reference/place-phone-call) allows you to try out this API. ### Place Call For Agent Request ```bash curl --location --request POST 'https://vcc-na12.8x8.com/api/v1/tenants/supertenantcsm01/calls' \ --header 'Authorization: Basic {encodedValue}' \ --header 'Content-Type: application/json' \ --data-raw '{ "agentId": "ag0VYyLh0YTsymdbtYIaaaa", "prefix": "1", "number": "6175551984", "callerId": "12015551212", "queueId": "598", "forceCall": true, "dialplanId": "12", "extTransactionData": [ { "name": "Name", "value": "Bilbo Baggins" }, { "name": "Loyalty Level", "value": "Gold" } ], "ctlUserData": [ { "name": "AccountRef", "value": "BB123987" }, { "name": "DueDate", "value": "2022-12-25" } ] }' ``` ### Place Call For AgentResponse Response Status will be 200 for successful requests. ```json { "reasons": [ "Call successfully initiated" ], "message": "OK", "interactionGuid": "int-184a0c5493f-nempJR9b0nXcZbChiZmtZkLCE-phone-00-acmecorp01" } ``` The `interactionGuid` in a successful response is a unique identifier for the placed call. This can be used in subsequent requests as the `interactionId` ## Set Transaction Codes for the Agent & Interaction Set the Transaction Codes for the interaction. ### Parameters **Method: PUT** #### Headers | Name | Required | Description | Example | | ------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of **Action Request Token** | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | | Content-Type | ✓ | Set the content type to application/json | application/json | #### Path | Name | Required | Description | Example | | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager. North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | version | ✓ | The API version. The current version is 1 resulting in. v1 | v1 | | tenantId | ✓ | The CC Tenant name to set the transaction codes on. Tenant name is generally the same as the username above. It can be located in CC Configuration Manager @ Home :: Profile :: Tenant Name | acmecorp01 | | interactionId | ✓ | The unique identifier for the call to set the transaction codes for. This is returned from place call as interactionGuid | int-184a0c5493f-nempJR9b0nXcZbChiZmtZkLCE-phone-00-acmecorp01 | | agentId | ✓ | The agentId of the agent to set the status for. This can be found in CC Configuration Manager "Users" add the "Internal Id" column, or in the "General" section of the user as "Internal Id" | ag0VYyLh0YTsymdbtYIaaaa | #### Body Body is an array named "selections" which contains objects defining the Transaction Code Lists and List Items to be added. #### Transaction Code List and Item Assignment Up to six transaction codes can be assigned to a transaction. Transaction Code Lists (TCL) are located in CC Configuration Manager "Transaction Codes". The TCL id and TCL item id are only visible/available via API. Single Item from Single ListTwo Items from Single ListTwo Items from Two Lists ```json { "selections": [ { "id": transaction_code_list_id_a, "codes": [ { "id": transaction_code_list_item_id_z } ] } } ``` ```json { "selections": [ { "id": transaction_code_list_id_a, "codes": [ { "id": transaction_code_list_item_id_y }, { "id": transaction_code_list_item_id_z } ] } } ``` ```json { "selections": [ { "id": transaction_code_list_id_a, "codes": [ { "id": transaction_code_list_item_id_y } ] }, { "id": transaction_code_list_id_b, "codes": [ { "id": transaction_code_list_item_id_w } ] } } ``` For a single Transaction Code List and Item the elements would be as follows | Name | Required | Description | Example | | ----------------------- | -------- | ----------------------------- | ------- | | selections[].id | ✓ | Transaction Code List Id | 12 | | selections[].codes[].id | ✓ | Transaction Code List Item Id | 234 | ### Set Transaction Codes Request Two items, from two lists ```bash curl --location --request PUT 'https://vcc-{ccPlatform}.8x8.com/api/v{version}/tenants/{tenantId}/calls/{interactionId}/agent/{agentId}/transaction-codes' \ --header 'Authorization: Basic {encodedValue}' \ --header 'Content-Type: application/json' \ --data-raw '{ "selections": [ { "id": 12, "codes":[ { "id": 234 } ] }, { "id": 2281, "codes":[ { "id": 7361 } ] } ] }' ``` ### Set Transaction Codes Response Response Status will be 200 for successful requests. ```json { "reasons": [ "Transaction codes successfully set" ], "message": "OK" } ``` ## End Call based on interactionId End the call based on the interactionId ### Parameters **Method: DELETE** #### Headers | Name | Required | Description | Example | | ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of Action Request Token. | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | #### Path | Name | Required | Description | Example | | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager. North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | version | ✓ | The API version. The current version is 1 resulting in. v1 | v1 | | tenantId | ✓ | The CC Tenant name of the tenant to perform the action on. Tenant name is generally the same as the username above. It can be located in CC Configuration Manager @ Home :: Profile :: Tenant Name | acmecorp01 | | interactionId | ✓ | The unique identifier for the call to end. This is returned from place call as interactionGuid | int-184a0c5493f-nempJR9b0nXcZbChiZmtZkLCE-phone-00-acmecorp01 | #### Query | Name | Required | Description | Example | | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | | endPostProcessing | ☐ | If present, the deleted interaction completes the assigned post processing. Default false. This will end post processing even if mandatory transaction codes have not been assigned. | true | ### End Call based on interactionId Request ```bash curl --location --request DELETE 'https://vcc-{ccPlatform}.8x8.com/api/v{version}/tenants/{tenantId}/calls/{interactionId}?endPostProcessing=true' \ --header 'Authorization: Basic {encodedValue}' ``` ### End Call based on interactionId Response Response Status will be 200 for successful requests. ```json { "reasons": [ "Ending call with interactionGuid=[int-184a0c5493f-nempJR9b0nXcZbChiZmtZkLCE-phone-00-acmecorp01] was successful" ], "message": "OK" } ``` ## Hang up call for agent based on agentId Hangs up the call for an agent. If the call is a conference or call was transferred it will not hang up for the other participants. ### Parameters **Method: DELETE** #### Headers | Name | Required | Description | Example | | ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of Action Request Token. | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | #### Path | Name | Required | Description | Example | | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager. North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | version | ✓ | The API version. The current version is 1 resulting in. v1 | v1 | | tenantId | ✓ | The CC Tenant name of the tenant to perform the action on. Tenant name is generally the same as the username above. It can be located in CC Configuration Manager @ Home :: Profile :: Tenant Name | acmecorp01 | | interactionId | ✓ | The unique identifier for the call to end. This is returned from place call as interactionGuid | int-184a0c5493f-nempJR9b0nXcZbChiZmtZkLCE-phone-00-acmecorp01 | | agentId | ✓ | The agentId of the agent to set the status for. This can be found in CC Configuration Manager "Users" add the "Internal Id" column, or in the "General" section of the user as "Internal Id" | ag0VYyLh0YTsymdbtYIaaaa | ### Hang up call for agent Request ```bash curl --location --request DELETE 'https://vcc-{ccPlatform}.8x8.com/api/v{version}/tenants/{tenantId}/calls/{interactionId}/agent/{agentId}' \ --header 'Authorization: Basic {encodedValue}' ``` ### Hang up call for agent Response Response Status will be 200 for successful requests. ```json { "reasons": [ "Hangup for agent [acmecorp01-ag0VYyLh0YTsymdbtYIaaaa-edc85374-bb3e-48ad-98fd-41d122701be6] leg in interaction [int-184a0c5493f-nempJR9b0nXcZbChiZmtZkLCE-phone-00-acmecorp01] was successful" ], "message": "OK" } ``` ## Free agent lines Free up agent lines in preparation for the next call. This will end the agent involvement in all their calls. If a call is a conference or call was transferred it will not hang up for the other participants. ### Parameters **Method: DELETE** #### Headers | Name | Required | Description | Example | | ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of Action Request Token. | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | #### Path | Name | Required | Description | Example | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager. North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | version | ✓ | The API version. The current version is 1 resulting in. v1 | v1 | | tenantId | ✓ | The CC Tenant name of the tenant to perform the action on. Tenant name is generally the same as the username above. It can be located in CC Configuration Manager @ Home :: Profile :: Tenant Name | acmecorp01 | | agentId | ✓ | The agentId of the agent to set the status for. This can be found in CC Configuration Manager "Users" add the "Internal Id" column, or in the "General" section of the user as "Internal Id" | ag0VYyLh0YTsymdbtYIaaaa | ### Free agent lines Request ```bash curl --location --request DELETE 'https://vcc-{ccPlatform}.8x8.com/api/v{version}/tenants/{tenantId}/agents/{agentId}/calls' \ --header 'Authorization: Basic {encodedValue}' ``` ### Free agent lines Response Response Status will be 200 for successful requests. ```json { "reasons": [ "Hangup all calls for agent [acmecorp01-ag0VYyLh0YTsymdbtYIaaaa-edc85374-bb3e-48ad-98fd-41d122701be6] was successful." ], "message": "OK" } ``` ## Free specific agent line Free up agent lines in preparation for the next call. This will end the agent involvement in all their calls. If a call is a conference or call was transferred it will not hang up for the other participants. ### Parameters **Method: DELETE** #### Headers | Name | Required | Description | Example | | ------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of Action Request Token. | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | #### Path | Name | Required | Description | Example | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager. North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | version | ✓ | The API version. The current version is 1 resulting in. v1 | v1 | | tenantId | ✓ | The CC Tenant name of the tenant to perform the action on. Tenant name is generally the same as the username above. It can be located in CC Configuration Manager @ Home :: Profile :: Tenant Name | acmecorp01 | | agentId | ✓ | The agentId of the agent to set the status for. This can be found in CC Configuration Manager "Users" add the "Internal Id" column, or in the "General" section of the user as "Internal Id" | ag0VYyLh0YTsymdbtYIaaaa | | lineNo | ✓ | The line number to free. Agent has Line 1 and Line 2 | 2 | ### Free specific agent line Request ```bash curl --location --request DELETE 'https://vcc-{ccPlatform}.8x8.com/api/v{version}/tenants/{tenantId}/agents/{agentId}/calls/line/{lineNo}' \ --header 'Authorization: Basic {encodedValue}' ``` ### Free specific agent line Response Response Status will be 200 for successful requests. ```json { "reasons": [ "Hangup all calls for agent [acmecorp01-ag0VYyLh0YTsymdbtYIaaaa-edc85374-bb3e-48ad-98fd-41d122701be6] was successful." ], "message": "OK" } { "reasons": [ "Hangup for agent [acmecorp01-ag0VYyLh0YTsymdbtYIaaaa-edc85374-bb3e-48ad-98fd-41d122701be6] leg on line [2] assigned to interaction [int-184a0c5493f-nempJR9b0nXcZbChiZmtZkLCE-phone-00-acmecorp01] was successful." ], "message": "OK" } ``` ## References [https://support.8x8.com/cloud-contact-center/8x8-contact-center/developers/8x8-contact-center-click-to-dial-api#extTransactionData](https://support.8x8.com/cloud-contact-center/8x8-contact-center/developers/8x8-contact-center-click-to-dial-api#extTransactionData) [https://support.8x8.com/cloud-contact-center/8x8-contact-center/developers/8x8-contact-center-click-to-dial-api#ctl_userdata](https://support.8x8.com/cloud-contact-center/8x8-contact-center/developers/8x8-contact-center-click-to-dial-api#ctl_userdata) --- ## CC Managing Agent Status This guide describes how to manage agent status via API ## Authentication CC Campaign APIs leverage the credentials from the Integration >> API Token area in Configuration Manager. These APIs use Basic Authentication. The username will be the "Username" value from this screen, it is generally the tenant name `Authorization :Basic encodedValue` Where encodedValue is base64encode(username:password) ![CC Request Action Token](../images/841df98-CC-Request-Action-Token.png "CC-Request-Action-Token.png") > 📘 If there are no token values > > If the token values are empty you can generate the token by clicking on 'New Token'. Once a token is generated, if you would like to generate a new one, please be aware that once a new token is generated, the old one will not work anymore for anybody. > ## Get Status for all agents ### Parameters **Method: GET** #### Headers | Name | Required | Description | Example | | :------------ | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------- | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of **Action Request Token** | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | #### Path | Name | Required | Description | Example | | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager.North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | version | ✓ | The API version. The current version is 1 resulting in. v1 | v1 | | tenantId | ✓ | The CC Tenant name of the tenant to list agent status for. Tenant name is generally the same as the username above. It can be located in CC Configuration Manager @ Home :: Profile :: Tenant Name | acmecorp01 | ### All Agents Status Request ```curl curl --location --request GET 'https://{ccPlatform}.8x8.com/api/v{version}/tenants/{tenantId}/agentstatus/agents' \ --header 'Authorization: Basic {encodedValue}' ``` ### All Agent Status Response ```json { "data": [ { "agent-id": "ag0VYyLh0YTsymdbtYIaaaa", "agent-status": 1, "name": "Ilya Workshard" }, { "agent-id": "ag0XgxWZr7TzqWFhxboBGj1g", "agent-status": 1, "status-code-list-id": -1, "status-code-item-id": 1, "name": "Jane Smith" }, { "agent-id": "ag1LmqEeEYRoOti9KjIi_Dng", "agent-status": 0, "name": "Paul Neverhere" }, { "agent-id": "ag64oyEUb_Sk6bxVB9P5ye5w", "agent-status": 5, "status-code-list-id": 801, "status-code-item-id": 1726, "status-code-item-short-code": "Train", "name": "Padma Othertask", "agent-sub-status": "none" } ] } ``` ## Get Status for single agent To request status for an individual agent simply append the appropriate agentId `/agents/agentId` ### Parameters **Method: GET** #### Headers | Name | Required | Description | Example | | :------------ | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------- | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of **Action Request Token** | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | #### Path | Name | Required | Description | Example | | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager.North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | version | ✓ | The API version. The current version is 1 resulting in. v1 | v1 | | tenantId | ✓ | The CC Tenant name of the tenant to list agent status for. Tenant name is generally the same as the username above. It can be located in CC Configuration Manager @ Home :: Profile :: Tenant Name | acmecorp01 | | agentId | ✓ | The agentId of the agent to set the status for. This can be found in CC Configuration Manager "Users" add the "Internal Id" column, or in the "General" section of the user as "Internal Id" | ag64oyEUb_Sk6bxVB9P5yaaa | ### Single Agent Status Request ```curl curl --location --request GET 'https://{ccPlatform}.8x8.com/api/v{version}/tenants/{tenantId}/agentstatus/agents/{agentId}' \ --header 'Authorization: Basic {encodedValue}' ``` ### Single Agent Status Response ```json { "agent-id": "ag64oyEUb_Sk6bxVB9P5yaaa", "agent-status": 5, "status-code-list-id": 801, "status-code-item-id": 1726, "status-code-item-short-code": "Train", "name": "Padma Othertask", "agent-sub-status": "none" } ``` ## Allowed Status Transitions - Red lines indicate single direction (not reversible via API) - Black lines indicate bidirectional - All other transitions are NOT supported via API ![Agent Status API Allowed Transitions](../images/838a466-Agent_Status_API_Allowed_Transitions.jpg "Agent Status API Allowed Transitions.jpg") [Agent status API Reference](/actions-events/reference/getagentsstatus) allows you to try out this API. ## Set Status for a single agent The status can only be set to and from the statuses defined in [allowed status change diagram](/actions-events/docs/cc-managing-agent-status#allowed-status-transitions) ### Parameters **Method: PUT** #### Headers | Name | Required | Description | Example | | :------------ | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------- | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of **Action Request Token** | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | | Content-Type | ✓ | Set the content type to application/json | application/json | #### Path | Name | Required | Description | Example | | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager.North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | version | ✓ | The API version. The current version is 1 resulting in. v1 | v1 | | tenantID | ✓ | The CC Tenant name of the tenant to list agent status for. Tenant name is generally the same as the username above. It can be located in CC Configuration Manager @ Home :: Profile :: Tenant Name | acmecorp01 | | agentId | ✓ | The agentId of the agent to set the status for. This can be found in CC Configuration Manager "Users" add the "Internal Id" column, or in the "General" section of the user as "Internal Id" | ag0VYyLh0YTsymdbtYIaaaa | #### Body | Name | Required | Description | Example | | :-------------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------ | | agent-status | ✓ | The status to assign to the agent. See [Status Values](/actions-events/docs/cc-managing-agent-status#status-values) | 5 | | status-code-list-id | ✓ | Status Code List Id is the id of the list. See [Status Code Lists and Items](/actions-events/docs/cc-managing-agent-status#status-code-lists-and-items) | 801 | | status-code-item-id | ✓ | The id of the item within the status code list. See [Status Code Lists and Items](/actions-events/docs/cc-managing-agent-status#status-code-lists-and-items) | 1722 | | status-code-item-short-code | ✓ | Short Text associated to the item within the status code list. See [Status Code Lists and Items](/actions-events/docs/cc-managing-agent-status#status-code-lists-and-items) | Meet | ### Set Single Agent Status Request ```curl curl --location --request PUT 'https://{ccPlatform}.8x8.com/api/v{version}/tenants/{tenantId}/agentstatus/agents/{agentId}' \ --header 'Authorization: Basic {encodedValue}' --header 'Content-Type: application/json' \ --data-raw '{ "agent-status": 5, "status-code-list-id": 801, "status-code-item-id": 1722, "status-code-item-short-code": "Meet" }' ``` ### Set Single Agent Status Response ```json { "reason": "OK", "change-status": 200, "message": "Agent status change successful" } ``` > 🚧 403 response code <API><Error>403 - Forbidden.</Error></API> > > Historically this capability has only enabled on request. If you receive response code 403 then please contact 8x8 support and request that your "Agent Status API" is enabled in OPSCON. > ## Set Status for Agents in bulk To set the status of multiple agents in a single request. - The requested status can be different for each agent. - Each agent request is independent and some requests can succeed and others fail within a single request. The outcome is delivered via a HTTP 207 Multi-Status response. ### Parameters **Method: PUT** #### Headers | Name | Required | Description | Example | | :------------ | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :----------------------------------------- | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of **Action Request Token.** | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | | Content-Type | ✓ | Set the content type to application/json | application/json | #### Path | Name | Required | Description | Example | | ---------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager.North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | version | ✓ | The API version. The current version is 1 resulting in. v1 | v1 | | tenantId | ✓ | The CC Tenant name of the tenant to list agent status for. Tenant name is generally the same as the username above. It can be located in CC Configuration Manager @ Home :: Profile :: Tenant Name | acmecorp01 | #### Body The body will contain an array of agents, each entry in the array will have the following: | Name | Required | Description | Example | | :--------------------------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------- | | agents.agent-id | ✓ | The agentId of the agent to set the status for. This can be found in CC Configuration Manager "Users" add the "Internal Id" column, or in the "General" section of the user as "Internal Id" | ag64oyEUb_Sk6bxVB9P5yaaa | | agents.agent-status | | The status to assign to the agent. See [Status Values](/actions-events/docs/cc-managing-agent-status#status-values) | 5 | | agents.status-code-list-id | ✓ | Status Code List Id is the id of the list. See [Status Code Lists and Items](/actions-events/docs/cc-managing-agent-status#status-code-lists-and-items) | 801 | | agents.status-code-item-id | ✓ | The id of the item within the status code list. See [Status Code Lists and Items](/actions-events/docs/cc-managing-agent-status#status-code-lists-and-items) | 1722 | | agents.status-code-item-short-code | ✓ | Short Text associated to the item within the status code list. See [Status Code Lists and Items](/actions-events/docs/cc-managing-agent-status#status-code-lists-and-items) | Meet | ### Set Bulk Status Request ```curl curl --location --request PUT 'https://<>.8x8.com/api/v1/tenants/<>/agentstatus/agents/bulk' \ --header 'Authorization: Basic <>' --header 'Content-Type: application/json' \ --data-raw '{ "agents": [ { "agent-id": "ag64oyEUb_Sk6bxVB9P5yaaa", "agent-status": 3, "status-code-list-id": 801, "status-code-item-id": 1725, "status-code-item-short-code": "Lunch" }, { "agent-id": "agAQDqmvKiRbG4ekigNSmbbb", "agent-status": 3, "status-code-list-id": 801, "status-code-item-id": 1725, "status-code-item-short-code": "Lunch" } ] }' ``` ### Set Bulk Status Response Response Status is `207` and the individual status of each agent status change is contained in the body of the response ```json { "data": [ { "agent-id": "ag64oyEUb_Sk6bxVB9P5yaaa", "reason": "OK", "change-status": 200, "message": "Agent status change successful" }, { "agent-id": "agAQDqmvKiRbG4ekigNSmbbb", "reason": "OK", "change-status": 200, "message": "Agent status change successful" } ] } ``` ## References ### Status Values that can be changed as explained in the diagram form the beginning of this documentation | Status | Label | Description | | :----- | :------------ | :--------------------------------------------------------------------------------------------------------------------- | | 1 | LOGGED_OUT | Currently logged out (and has previously logged in at least one time) | | 3 | ON_BREAK | On break status, could be manually selected or automatically on break based on initial login or missing a transaction. | | 4 | WAIT_TRANSACT | Available | | 5 | WORK_OFFLINE | Working offline | ### Status Values that can be returned by the GET operations but cannot be ammended by the SET operation | Status | Label | Description | | :----- | :--------------- | :----------------------------------------------------------------------------------- | | 0 | UNKNOWN | Generally the Agent has never logged in | | 2 | LOGGED_IN | | | 6 | TRANSACT_OFFERED | | | 7 | PROCESS_TRANSACT | | | 8 | POST_PROCESS | | | 9 | BUSY | | | 10 | DIRECT_CALL | Note: If a direct call is also marking the agent Busy then the status will be 9 BUSY | | 11 | ON_EMAIL | | ### Status Code Lists and Items Status Code Lists are assigned to Agent Groups via CC Configuration Manager. These lists allow for a specific reason for the status to be selected by the agent or in this case assigned via the API. **status-code-list-id** this is the id of the list itself: This can be identified either by using the [Get Agent Status](#get-status-for-all-agents) or via the stats API. **status-code-item-id** this is the id of the list itself: This can be identified either by using the [Get Agent Status](#get-status-for-all-agents) to return the values for an agent in a known state or via the stats API. **status-code-item-short-code** This can be found in CC Configuration Manager under: Status Codes, by selecting the required Status Code List, then under "Codes" referencing the "Short Codes" column, or by using the [Get Agent Status](#get-status-for-all-agents) to return the values for an agent in a known state or via the stats API. --- ## CC Managing Campaign Records ## TODO > ⚠️ **Beta customers — use the new Contact Center Campaigns API** > > If you are part of the [Contact Center Campaigns API (BETA)](./cc-campaigns/overview.md), use that API instead of the one documented on this page. The two APIs manage entirely separate sets of campaigns — see [Relationship to the legacy Contact Center Dynamic Campaigns API](./cc-campaigns/overview.md#relationship-to-the-legacy-contact-center-dynamic-campaigns-api) for details. > 📘 **Prerequisites** > > * The Campaign MUST be configured as a Dynamic Campaign > * Records MUST exist in the CC CRM to be added to a campaign. > ## Overview The 8x8 Contact Center Dynamic Campaigns API can be used to accomplish one or more of the following. * Adds and removes records from an active campaign * Sends records to a specified campaign via the API * Adds records to a live campaign * Removes records from a campaign so they are not dialed again * Schedules a call with a possible **maximum of 7 days in advance** * Uploads for a **maximum of 5 million records** ## Authentication CC Campaign APIs leverage the credentials from the Integration >> API Token area in Configuration Manager. These APIs use Basic Authentication. The username will be the "Username" value from this screen, it is generally the tenant name The Data Request Token will the password `Authorization :Basic encodedValue` Where encodedValue is base64encode(username:password) ![CC Request Action Token](../images/a542cf0-CC-Request-Action-Token.png "CC-Request-Action-Token.png") ## Working with Campaign Records ## Add Records to Campaign ### Parameters **Method: POST** #### Headers | Name | Required | Description | Example | | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------ | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of **Action Request Token**. | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | | Content-Type | ✓ | Set content type for body to application/json | application/json | #### Path | Name | Required | Description | Example | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager.North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | campaignId | ✓ | The id of the campaign to get the status for. Can be located in CC Configuration Manager "Campaigns" and adding the "Campaign ID" column, or within a specific campaign as part of "Properties", "General Properties" | 125 | #### Body Body is an unnamed array of customer records. See [Add Campaign Record Request](/actions-events/docs/cc-managing-campaign-records#add-campaign-record-request) for a full example. Customer Record: | Name | Required | Description | Example | | ------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------- | -------------------------- | | customer-id | ✓ | customer-id is the ACCOUNTNUM of the customer from the CC CRM. This can be found in the Agent Workspace OR via the CC CRM API. | 10003629 | | schedule-date-time | ☐ | Optional, scheduled/desired time for call which will influence the time the record is processed. ISO8601 datetime format. | "2022-08-29T09:00:00.000Z" | ### Add Campaign Record Request ```bash curl --location --request POST 'https://vcc-{ccPlatform}.8x8.com/api/tstats/campaigns/{campaignId}/customers' \ --header 'Content-Type: application/json' \ --header 'Authorization: Basic {encodedValue}' --data-raw '[ { "customer-id": 10003629, "schedule-date-time": "2022-07-14T09:00:00.000Z" }, { "customer-id": 10003621, "schedule-date-time": "2022-07-14T09:00:00.000Z" } ]' ``` ### Add Campaign Record Response (showing additional responses) Response is HTTP 207 Multi-Status response. Each Customer has it's own status represented. ```json [ { "customer-id": 10003629, "schedule-date-time": "2016-08-29T09:00:00.000Z", "http-status": 200 }, { "customer-id": 1000001, "http-status": 200 }, { "customer-id": 10003621, "http-status": 400, "message": "Duplicate customer" }, { "customer-id": 1000777, "schedule-date-time": "2016-08-29T 09:00:00.000Z", "http-status": 400, "message": "Invalid schedule-date-time format - please use ISO 8601 format" }, { "customer-id": null, "http-status": 400, "message": "Invalid customer-id" }, { "customer-id": 9, "http-status": 404, "message": "The customer-id has not been found." }, { "customer-id": 10000, "schedule-date-time": "2016-08-29T09:00:00.000Z", "http-status": 400, "message": "The subject schedule-date-time is in the past." }, { "customer-id": 1000778, "schedule-date-time": "2016-08-29T09:00:00.000Z", "http-status": 400, "message": "The subject schedule-date-time is outside the campaign schedule from 2016-08-27T09:00:00.000Z to 2016-08-28T09:00:00.000Z." }, { "customer-id": 100002, "schedule-date-time": "2016-08-29T09:00:00.000Z", "http-status": 400, "message": "The subject schedule-date-time must be within 7 days ahead of a future period." } ] ``` ## View Records in Campaign ### Parameters **Method: GET** #### Headers | Name | Required | Description | Example | | ------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of **Data Request Token**. | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | #### Path | Name | Required | Description | Example | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager. North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | campaignId | ✓ | The id of the campaign to get the status for. Can be located in CC Configuration Manager "Campaigns" and adding the "Campaign ID" column, or within a specific campaign as part of "Properties", "General Properties" | 125 | ### View Campaign Records Request ```bash curl --location --request GET 'https://vcc-{ccPlatform}.8x8.com/api/stats/campaigns/{campaignId}/records.json' \ --header 'Authorization: Basic {encodedValue}' ``` ### View Campaign Record Response The success response code is HTTP 200. Each Record will have its own status and information. ```json { "records": { "record": [ { "campaign-name": "BOC Admin Demo", "campaign-id": 1, "record-id": 10000000, "phone-list": "*Phone Number|5551234567", "status": 3, "status-code": 0, "ext-trans-data": "", "disposition-code": 1002 }, { "campaign-name": "BOC Admin Demo", "campaign-id": 1, "record-id": 10000001, "phone-list": "*Phone Number|5557654321", "status": 3, "status-code": 0, "ext-trans-data": "", "disposition-code": 1002 } ] } } ``` **There are several predefined values** **Record status:** 0 = New 1 = Queued 2 = Accepted 3 = Completed 4 = Scheduled **Status code:** 0 = Default 1 = Max Attempt Reached 2 = Skipped 3 = No Phone Number 4 = Invalid Phone Number **Disposition code:** 1000 = None 1001 = Try Again 1002 = Scheduled Call Back ## Delete Record from Campaign ### Parameters **Method: DELETE** #### Headers | Name | Required | Description | Example | | ------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of **Data Request Token**. | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | #### Path | Name | Required | Description | Example | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager. North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | campaignId | ✓ | The id of the campaign to get the status for. Can be located in CC Configuration Manager "Campaigns" and adding the "Campaign ID" column, or within a specific campaign as part of "Properties", "General Properties" | 125 | | customerId | ✓ | customer-id is the ACCOUNTNUM of the customer from the CC CRM. This can be found in the Agent Workspace OR via the CC CRM API. | 10003629 | ### Delete Campaign Record Request ```bash curl --location --request DELETE 'https://vcc-{ccPlatform}.8x8.com/api/tstats/campaigns/{campaignId}/customers/{customerId}' \ --header 'Authorization: Basic {encodedValue}' ``` ### Delete Campaign Record Response The success response code is HTTP 204 (No Content) --- ## CC Managing Campaign Status > ⚠️ **Beta customers — use the new Contact Center Campaigns API** > > If you are part of the [Contact Center Campaigns API (BETA)](./cc-campaigns/overview.md), use that API instead of the one documented on this page. The two APIs manage entirely separate sets of campaigns — see [Relationship to the legacy Contact Center Dynamic Campaigns API](./cc-campaigns/overview.md#relationship-to-the-legacy-contact-center-dynamic-campaigns-api) for details. > 📘 **Prerequisites** > > * The Campaign MUST be configured as a Dynamic Campaign in Configuration Manager > ## Authentication CC Campaign APIs leverage the credentials from the Integration >> API Token area in Configuration Manager. These APIs use Basic Authentication. The username will be the "Username" value from this screen, it is generally the tenant name **For read APIs the Data Request Token will the password** **For write APIs the Action Request Token will be the password** `Authorization :Basic encodedValue` Where encodedValue is base64encode(username:password) ![CC Request Action Token](../images/a542cf0-CC-Request-Action-Token.png "CC-Request-Action-Token.png") ## Working with Campaign Status ### Status Assignment **RUNNING** - Change status to Running from one of the allowed initial states **PAUSED** - Change status to Paused from one of the allowed initial states **PURGED** - Change status to Purged from one of the allowed initial states **STOPPED** - Change status to Stopped from one of the allowed initial states. **Stopped is a FINAL STATE** ### Allowed Campaign Status Changes No other transitions are allowed, once a campaign is Stopped it cannot be restarted. ![1621](../images/8962ed0-Campaign_Manager_state_machine.jpg "Campaign Manager state machine.jpg")Allowed Campaign State Transitions ## Get Campaign Status > 🚧 **URLS vary in this section with an additional 't' in setting campaign status** > > For **Getting** the campaign status the url contains stats: /api/stats/campaigns/ > > For **Setting** the campaign status the url contains tstats: /api/**t**stats/campaigns/ > > ### Parameters **Method: GET** #### Headers | Name | Required | Description | Example | | ------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of **Data Request Token**. | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | #### Path | Name | Required | Description | Example | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager. North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | campaignId | ✓ | The id of the campaign to get the status for. Can be located in CC Configuration Manager "Campaigns" and adding the "Campaign ID" column, or within a specific campaign as part of "Properties", "General Properties" | 125 | ### Campaign Status Request ```bash curl --location --request GET 'https://vcc-{ccPlatform}.8x8.com/api/stats/campaigns/{campaignId}.json' \ --header 'Authorization: Basic {encodedValue}' ``` ### Campaign Status Response ```json { "campaign": { "campaign-name": "My Callbacks", "campaign-id": 6561, "enabled": "Y", "status": 3, "no-of-records": 18, "caller-id": 13125555068, "queue-id": 598, "start-time": "", "end-time": "", "actual-run-time": "2022-10-17T22:31:02+01:00", "actual-stop-time": "", "daily-start-time": "", "daily-end-time": "", "retry-interval": 30, "max-retry": 1, "abandon-max-retry": "", "max-ring-time": 15, "integration-type": "contactual", "calling-window": 2, "schedule-name": "'5-6pm Eastern'", "timezone": "EST5EST", "dynamic-campaign": "Y", "sequential": "N" } } ``` ## Change Campaign Status > 🚧 **URLS vary in this section with an additional 't' in setting campaign status** > > For **Getting** the campaign status the url contains stats: /api/stats/campaigns/ > > For **Setting** the campaign status the url contains tstats: /api/**t**stats/campaigns/ > > ### Parameters **Method: POST** #### Headers | Name | Required | Description | Example | | ------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of username and the password is the value of **Data Request Token**. | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | #### Path | Name | Required | Description | Example | | ---------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | ccPlatform | ✓ | Contact Center platform can be found in the url when accessing CC Configuration Manager. North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | na12 | | campaignId | ✓ | The id of the campaign to get the status for. Can be located in CC Configuration Manager "Campaigns" and adding the "Campaign ID" column, or within a specific campaign as part of "Properties", "General Properties" | 125 | #### Body | Name | Required | Description | Example | | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | | status | ✓ | The status to change the campaign state to: See [Status Assignment](/actions-events/docs/cc-managing-campaign-status#status-assignment) and [Allowed Campaign Status Changes](/actions-events/docs/cc-managing-campaign-status#allowed-campaign-status-changes) - RUNNING- PAUSED- PURGED- STOPPED | RUNNING | ### Change Campaign Status Request ```bash curl --location --request POST 'https://vcc-{ccPlatform}.8x8.com/api/tstats/campaigns/{campaignId}' \ --header 'Content-Type: application/json' \ --header 'Authorization: Basic {encodedValue}' --data-raw '{ "status": "RUNNING" }' ``` ### Change Campaign Status Response ```json { "message": "Campaign status changed." } ``` ## References ### Campaign Status List | Campaign Status ID | Description | | ------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | | 0 | **New** Campaigns that are newly created that have not been started or scheduled. | | 1 | **Manual Started** Campaigns that have been manually started. | | 2 | **Scheduled** Campaigns with scheduled start time that have been started but start time has not been reached. | | 3 | **Manual Running** Campaigns that have been started manually without schedule. These campaigns go into running state right after it is started | | 4 | **Schedule Running** Campaigns that have reached scheduled start time and successfully run. These campaigns are still in running states. | | 5 | **Manual Stopped** Campaigns that have been manually stopped by clicking stop button | | 6 | **Completed (Stopped)** Campaigns have all records completed. These campaigns stopped. | | 7 | **Schedule Stopped** Campaigns that have been stopped due to scheduled stop time has been reached. | | 8 | **Manual Paused** Campaigns that have been paused. All the queued records will continue to be processed. | | 9 | **Schedule Paused** | | 10 | **Manual Purged** Campaigns that have been paused and records that are in the queues have been flushed as appropriate. | | 11 | **Schedule Purged** | --- ## Channel After creating a webhook you can then create a channel. The channel contains a webhook that passes the outbound messages and notifications. The channel has a **[chat queue](https://docs.8x8.com/8x8WebHelp/VCC/configuration-manager-unified-login/content/queuespageoverview.htm)** (ideally with an agent assigned), for routed inbound conversations. A Channel can be created either via UI or via API. ## Create a channel using Configuration Manager 1. Access [Configuration Manager](https://docs.8x8.com/8x8WebHelp/VCC/configuration-manager-general/content/cfgoverview.htm). 2. Go to **Channels > Chat API** ![image](../images/9e23ddb-Screenshot_2021-07-07_at_11.51.29.png "Screenshot 2021-07-07 at 11.51.29.png") 3. Click **New Channel** ![image](../images/72b5dec-Screenshot_2021-07-07_at_11.52.01.png "Screenshot 2021-07-07 at 11.52.01.png") 4. Complete the **Name**, **Description**, **Webhook** and **Queue** fields 5. Click **Save** ## Create a channel using API 1. Obtain your **[API key](/actions-events/docs/api-key)** 2. Call the **[Create a Channel endpoint](/actions-events/reference/createchatapichannel-1)** --- ## API Key(Docs) You can now get an API key via the 8x8 [Admin Console.](https://admin.8x8.com) ![3348](../images/6c0431c-Screen_Shot_2020-11-17_at_1.45.17_PM.png "Screen Shot 2020-11-17 at 1.45.17 PM.png")Admin Console Home Page 1. Click on API Keys ![3360](../images/b68c40e-Screen_Shot_2020-11-17_at_1.45.29_PM.png "Screen Shot 2020-11-17 at 1.45.29 PM.png") 2. Click on Create App ![3358](../images/0e52013-Screen_Shot_2020-11-17_at_1.45.49_PM.png "Screen Shot 2020-11-17 at 1.45.49 PM.png") 3. Add A new Application. Here you'll give it a name, and under API products select "Chat" to create an API key enabled for Chapi. --- ## Chat Gateway ## Overview The 8x8 Contact Center Chat Gateway enables you to provide chat services with your chat app and chatbot. The Chat Gateway API provides a server-to-server integration that enables a client application to provide chat services while retaining full control of both the user interface and experience. The Chat Gateway API also supports chatbot integration so you can have direct conversations to further enhance your customer service deflection capabilities (e.g., chatbots facilitate the initial customer engagement which would then be transferred to a live agent as needed). Enhanced automation such as chatbots drives agent efficiencies and reduces new agent training costs. Your customers can use their phones or digital devices to communicate and engage with your service agents or representatives through messaging. The types of communication channels include: * Mobile app-based chat * Desktop web chatbots * Messaging via integrations with other apps or platforms The API routes customer communications to your Contact Center enabled agents from initial engagement. This provides continuity of experience and ensures customer confidence in your enterprise as you proactively respond to their inquiries. Using Contact Center with the Chat Gateway API enables you to scale and manage your customer service engagements. The Chat Gateway enables you to: * Initiate (chatbot) conversations with your customers and then hand them over to available agents * Achieve 2-way communication by using web API calls and callbacks to and from Contact Center. You are able to forward customer messages and are notified when your agents reply to customers. * Route conversations straight into the preferred queue and let Contact Center handle the agent assignment based on availability and experience. Additional functions include: • Screen-pops with relevant CRM customer information ```text • Pass the context of the bot conversation to the agent • Pass the full transcription of the bot conversation to the agent • Update CRM information while a customer is queuing • Display of FAQs or prepared responses • Auto-translation of customer messages • Standardized transaction codes while wrapping up customer interactions • Transfer chats to other queues or departments ``` ## Use cases A retail client wants to be able to add the ability for their webchat customers to see where their order is, without the need to speak to an agent. However, they want to be able to hand over other queries to an agent. The client can engage with a 3rd party conversational AI provider to provide a chatbot, then use their chat widget where they can provide the self-serve for customers on the progress of their orders. When the query needs to be handed over to an agent, the chatbot will connect into the chat gateway, which allows them to route the customer through to a queue, which will then be received by an agent. When the agent accepts this interaction, they will receive information from the chatbot on what the query is about and be able to see a full transcript of the chatbot conversation. The agent can then communicate with the customer to help solve their query. ## Example flow with vendor channels A customer uses a 3rd party vendors widget on a web site The 3rd party vendor provides self serve capability to this customer The customer wants to speak to an agent 3rd party vendor connects to the 8x8 chat gateway 3rd party vendor selects the queue to route The customer is then queue and connected with an agent with chat history and customer information provided from the 3rd party vendor ![Chat Gateway flow](../images/9673c4a-Chat_Gateway_flow.jpg "Chat Gateway flow.jpg") ## Example flow with 8x8 digital channels A customer uses an 8x8 widget on a web site 8x8 route this query through to a 3rd party vendor for self serve During self serve, the bot recognises that the customer wants to speak to an agent 3rd party vendor selects the queue to route The customer is then queue and connected with an agent with chat history and customer information provided from the 3rd party vendor ![Chat Gateway flow](../images/5a322f6-Chat_Gateway_flow_1.jpg) --- ## Chat Language ## Introduction Using the webchat, you may have a customer that is comforable speaking in their own language, so we offer realtime translation on this chat, you can find more on this service [here](https://docs.8x8.com/8x8WebHelp/contact-center/agent-workspace/Content/aw/handle-multilingual-chats.htm) When using the webchat API, you can decide to set the language, other than it using the form or the customer browser language. The list of supported languages are: English: en Russian: ru German: de Japanese: ja Spanish: es French: fr Portuguese: pt Italian: it Polish: pl Croatian: hr Hindi: hi Dutch: nl Arabic: ar Danish: da Korean: ko Norwegian: no Swedish: sv Vietnamese: vi Welsh: cy Thai: th Simplified Chinese: zh-CN Traditional Chinese: zh-TW ### Script config Here is an example of where the language you to set for the customer is French. ```javascript function fn(chatApp) { chatApp.setCustomerLanguage("fr"); } ``` To add this, you need to add it at the bottom of the script in this area here ```html })( --ADD THE FUNCTION CHATAPP CODE HERE ); ``` Then, the full script will look like this ```javascript ``` ### Agent view When the query is routed through to an agent, they will be able to see the data that was passed into the webchat, in both the interaction panel ![Chat Gateway flow](../images/4ecdf7d0b8c0a0c8bbed9d94570493ba0c5a125d6c6d013e887ca87e5629e64d-Agent.png "Chat Gateway flow.jpg") Here, it what the customer see's so the full conversation is in french ![Chat Gateway flow](../images/df3d03b1178d6797084eabe3944427d4ae8324ca6ee9016cf2ab398966fd2961-Customerchat.png "Chat Gateway flow.jpg") ### Important Notes * The chat language can be set at any time before starting a chat (when the customer enters a queue) and you can change it as many time as you want, only the last value will be considered. * Same elements that are translated with pre-chat form set language or default browser/tenant language will be translated here as well. * Questions in the pre-chat form and quick replies are not currently translated --- ## Things you should know Here are some things you should know about interacting with the Chat API: 1. The API user can see all public rooms and needs to be invited to private rooms. 2. The API user can read messages from all rooms it has access to, including private rooms it's been invited to. 3. Anyone with access to the API can use all of it's endpoints. 4. Your API key is limited to 5 requests per second. 5. Chapi is officially supported in the US region. --- ## Workflow - Connecting 3rd party channel ## Prerequisites To use the Chat Gateway you must have the following: * At least one configured queue so that any interactions coming from your customers can be routed to your agents. * At least one agent is assigned to your queue, so they can pick up the queued interactions and connect with your customers. * Know the Queue ID. Information on how to do this can be found [here](https://docs.8x8.com/8x8WebHelp/VCC/configuration-manager-vovcc/content/queuechatqueue.htm?Highlight=chat%20queue) * A client application that calls the Chat API and listens for messages (i.e., events) that are returned. ## Setup flow To set up the Chat Gateway do the following: * **API key** Follow the flow to get your access token [API Key](/actions-events/docs/api-key) Next, we need to create the webhook, so that data can be sent between 8x8 and the application and also the channel, which allows the webhook to connect to 8x8. There are two flows, via the API and in configuration manager in 8x8 Contact Center * **Create webhook** - [API method](/actions-events/reference/createwebhook-1) Go to Configuration Manager - Integration - webhook ![image](../images/14c401f-Webhook111.png) Then select Add webhook and populate with the details Name - this is the name you want to give the webhook URL - this is the webhook of the application that you want to communicate with the Chat Gateway Chat API Version - This needS to be Chat Gateway ![image](../images/42bf824-Webhook3.png) Now you can create a channel **Create a channel** - [API method](/actions-events/reference/createchatapichannel-1) Go to Configuration Manager - Channel - Chat API ![image](../images/cfd6d87-ChatGatewayChannel.png) Select new channel, then populate the details Name - the name you want to give the channel Description - the description you want to give the channel Webhook - select the webhook you have created in the step above, or a different one if you need Queue - This is a queue that the channel will route to, however this can be changed in the conversation flow if needed ![image](../images/c093697-NewChannel1.png) **Create conversation** [API](/actions-events/reference/createcctransaction-1) When creating the conversation, the channnel ID and the API key which have been created earlier in the flow are needed. Information that can be passed across when creating the conversation **User object** These allows you to give information about the customer to the agent and also populate the 8x8 Native CRM, in this example below Language - the language of the customer Name - name of the customer Email - email address of the customer (this can be used to screen pop the customer record in the native CRM or an External CRM) Phone - phone number of the customer ```json { "user": { "language": "en", "name": "James Jones", "email": "user@example.com", "phone": "07917846011" } } ``` **User object - Additional properties** This allows extra information to be passed across to the agent, specifically customer values that can be conveyed using the format :customValue anyKey: anyValue.It is essential to recognize that customer values being passed must include both a key and a corresponding value. In the example below, the bot is telling the agent that the customer is authenticated, they were asking about their balance and it's regarding their credit account ```json { "additionalProperties": [ { "key": "Authenticated", "value": "yes" }, { "key": "Intent", "value": "balance" }, { "key": "AccountType", "value": "credit" } ] } ``` **User object - History** This allows the content of the conversation that has happened between the bot and the agent to be passed across to the agent. In the example below, it shows the conversation between the agent and bot regarding a balance which will be presented to the agent ```json { "history": { "messages": [ { "authorType": "user", "text": "I would like my balance please" }, { "authorType": "bot", "text": "Sure, please let me your account number" }, { "authorType": "user", "text": "My account number is 1234" }, { "authorType": "bot", "text": "Your balance is £12.34" } ] } } ``` Example JSON of the user objects together ```text curl --request POST --url [https://api.8x8.com/chat-gateway/v1/conversations](https://api.8x8.com/chat-gateway/v1/conversations) --header 'accept: application/hal+json' --header 'content-type: application/json' --data ' { "user": { "language": "en", "name": "James Jones", "email": "user@example.com", "phone": "013453403332", "additionalProperties": [ { "key": "Authenticated", "value": "yes" }, { "key": "Intent", "value": "balance" }, { "key":" AccountType", "value": "credit" } ] }, "assignment": { "type": "queue", "id":" 123" }, "history": { "messages": [ { "authorType": "user", "text": "Hello, can I get my balance please" }, { "authorType": "bot", "text": "Sure, what is your account number" }, { "authorType": "user", "text": "123456" }, { "authorType": "bot", "text": "Your balance is £12.34" }, { "authorType": "user", "text": "Thanks, please can I speak to an agent " }, { "authorType": "bot", "text": "Sure, connecting you now" } ] }, "channelId": "f5odm43occnifcdcsa" } ``` In the history in the user objects, you can also add **User object - History - attachments** When adding the user history, attachments that have been sent between the bot and the customer can also be added to this conversation **User object - History - adaptive cards** When adding the user history, adaptive cards that have been sent between the bot and customer can be added to the conversation. More information on adaptive cards can be found here - [MS Adaptive cards](https://learn.microsoft.com/en-us/adaptive-cards/) Once the conversation is created an activity will be sent for # **`QUEUED`** ```json { "eventType": "QUEUED", "conversationId": "ID-0", "timestamp": 0, "data": { "queueId": "string", "queueName": "string" } } ``` When the conversation is queued, this information can still be updated and the queue can be changed using - **Update conversation** [Update conversation](/actions-events/reference/putcctransaction) Once the conversation has reached an agent, the follow notification will be received from # **`AGENT JOINED`** ```json { "eventType": "AGENT_JOINED", "messageType": "SYSTEM", "conversationId": "ID-0", "timestamp": 0, "agentId": "string", "agentName": "string" } ``` Then, when the agent is typing a message, the following activity will be sent # **`ACTIVITY`** ```json { "eventType": "ACTIVITY", "conversationId": "ID-0", "timestamp": 0, "data": { "name": "typing", "value": { "users": [ { "type": "agent", "id": "string" } ] } } } ``` The conversation will then be ongoing between the agent and the customer. An agent sends a message, this will be received on the message activity with the conversationID # **`MESSAGE`** ```json { "eventType": "MESSAGE", "conversationId": "ID-0", "timestamp": 0, "data": { "isEcho": true, "sender": { "id": "string", "type": "" } ``` Then, a message can be sent back in, using the conversation ID, ```json "authorType": "user", "text": "Hello, I'm sending a message" ``` ## Note > 📘 **To enhance the experience of the customer, we recommend you leverage these Contact Center API's as well -** > > [Real Time Statistics Reporting API](/analytics/docs/cc-realtime-statistics), to retrieve customer information, make routing decisions. > > [Tenant Schedule API](https://support.8x8.com/cloud-contact-center/8x8-contact-center/developers/what-is-the-8x8-contact-center-tenant-provisioning-api), to ensure the Contact Center is open and receiving queries before handing the query off to an agent. > > --- ## Content Security Policy CSP directives a customer's website needs to embed the 8x8 WebChat v2 widget. The widget runs in an `iframe srcdoc`, so **the embedding page's CSP applies**. ## How the widget loads Why the policy looks the way it does: - **8x8 does not send a CSP of its own.** The widget is injected as a **sandboxed `iframe srcdoc`**, with these sandbox permissions: `allow-scripts allow-downloads allow-modals allow-popups allow-popups-to-escape-sandbox`. - Because the iframe is `srcdoc`, **your page's CSP is the only policy the browser enforces on the widget** — you cannot relax it from inside the iframe. - All widget scripts load from **external files** (no inline ` ``` Then, the full script will look like this ```html ``` ### Agent view When the query is routed through to an agent, they will be able to see the data that was passed into the webchat, in both the interaction panel - ![image](../images/e09dd32dd695c7ff0d8bfee637385855858337809466ace9222db2591c17f439-Interactionpanel.png "Chat Gateway flow.jpg") Also the chat panel ![image](../images/18f28912a0053c2e9352348fee117bc8e54ea378b92c14fadcb0707fc085c395-Chatpanel.png "Chat Gateway flow.jpg") ### Important Notes * The customer information can be set on any myProxy lifecycle subscriber hook, but it will only be actually sent to the server when the customer is added to a queue. * Only primitive data types (strings, numbers and booleans) are allowed as values. Trying to set the customer information with non-primitive data types (e.g., objects, arrays and functions) will result in console warnings, and their values will be discarded. * Setting a customer information already previously set will overwrite that information, unless the new value is undefined. If the new value is null, that information will be removed from the customer information and will not be sent to the server from that moment on, unless it is set again with a non-null, defined value. * The customer information key:value pair is limited in 100 characters for the key and 500 character for the value. Any key:pair bigger than the specified value will be ignored. ### Troubleshooting * Make sure that, when trying to remove properties from the customer information without resetting all its properties, the passed properties have null values (and not undefined), as setting undefined values to properties do not have any effect on the final customer information. * If the message Customer info is not of a primitive type and will be discarded is shown in the browser console when trying to set the customer information, it means that the logged property value is of a data type unsupported by the embedded chat. Only primitive data types (strings, numbers and booleans) are currently allowed. * If the message Customer info key is too big and will be discarded is shown in the browser console when trying to set the customer information, it means that the key string length is too big. Only strings with length less than 100 characters are allowed. * If the message Customer info value is too big and will be discarded is shown in the browser console when trying to set the customer information, it means that the value string length is too big. Only strings with length less than 500 characters are allowed. --- ## Dark mode example Here, is a full script example, of where the colours have been configured so it shows in dark mode ```html ``` --- ## End chat ## Introduction There are scenarios where you may need to programmatically end a webchat session from your website. For example, when a customer navigates away from a support page, logs out of their account, or when your application determines the conversation should be closed based on business logic. The `endChat()` method allows you to terminate the chat session via the API, without requiring the customer to click the close button inside the widget. ### Script config Here is an example of how to end the chat programmatically ```javascript function fn(chatApp) { window.chatApp = chatApp; } // Later, when you want to end the chat: window.chatApp.endChat(); ``` A common use case is ending the chat when the customer logs out of your website ```javascript function fn(chatApp) { window.chatApp = chatApp; } document.getElementById('logout-button').addEventListener('click', function () { window.chatApp.endChat(); }); ``` To add this, you need to add it at the bottom of the script in this area here ```html })( --ADD THE FUNCTION CHATAPP CODE HERE ); ``` Then, the full script will look like this ```html ``` ### Behaviour The `endChat()` method works differently depending on the current state of the webchat: * **During pre-chat stages** (button, invitation, or pre-chat form): The chat UI is closed immediately. No API calls to the server are made since no conversation exists yet. * **During an active chat session**: The active interaction is terminated via the server API, the chat window is closed, and the agent is notified that the conversation has ended. ### Detecting how the chat ended When using `endChat()`, the `onAppEnd` callback receives an `endChatReason` parameter that indicates whether the chat was ended by the customer (via the close button) or programmatically (via the API). See [Event callbacks](event-callbacks) for details on all available callbacks. ```javascript chatApp.setProxy({ onAppEnd: function (endChatReason) { if (endChatReason === 'chat-api') { console.log('Chat was ended programmatically'); } else { console.log('Chat was ended by the user'); } } }); ``` ### Waiting for the chat to fully terminate The `endChat()` method initiates the termination process but returns immediately — it does not wait for the server to finish closing the conversation. If your page navigates away or redirects before the chat has fully terminated, the server-side cleanup may not complete, which can leave the interaction in an unfinished state for the user. To avoid this, use the `onAppEnd` callback to wait for the chat to fully close before performing any navigation or page unload: ```javascript function fn(chatApp) { window.chatApp = chatApp; chatApp.setProxy({ onAppEnd: function (endChatReason) { if (endChatReason === 'chat-api') { // Safe to navigate — the chat has fully terminated window.location.href = '/logged-out'; } } }); } document.getElementById('logout-button').addEventListener('click', function () { window.chatApp.endChat(); // Do NOT redirect here — wait for onAppEnd instead }); ``` ### Important Notes * The `endChat()` method can be called at any point during the chat lifecycle, including before the chat window is opened. * When called during an active session, the chat ends without showing a confirmation dialog to the customer. * Always listen for `onAppEnd` before redirecting or unloading the page, to ensure the conversation is properly closed on the server. See [Event callbacks](event-callbacks) for details. * The method returns the `chatApp` instance, so it can be chained with other methods. * If `endChat()` is called when no chat is active, it has no effect. --- ## Event callbacks ## Introduction The webchat widget emits lifecycle events as the chat progresses through different stages. Using the `setProxy()` method, you can register callback functions to respond to these events on your website. This is useful for analytics tracking, updating your page UI, or coordinating the chat lifecycle with your application logic. ### Script config Here is an example of how to register event callbacks ```javascript function fn(chatApp) { chatApp.setProxy({ onAppStart: function () { console.log('Chat widget initialized'); }, onAppEnd: function (endChatReason) { console.log('Chat ended, reason:', endChatReason); }, onOpenChat: function () { console.log('Chat window opened'); } }); } ``` You only need to register the callbacks you are interested in. Any callbacks not provided will be ignored. To add this, you need to add it at the bottom of the script in this area here ```html })( --ADD THE FUNCTION CHATAPP CODE HERE ); ``` Then, the full script will look like this ```html ``` ### Available callbacks | Callback | Parameters | Description | |---|---|---| | `onAppStart` | None | Fired when the chat widget initialization completes. | | `onAppEnd` | `endChatReason` | Fired when the chat widget closes. The `endChatReason` parameter is either `'user'` (customer clicked the close button) or `'chat-api'` (ended programmatically via `endChat()`). | | `onOpenInvitation` | None | Fired when the invitation UI is displayed to the customer. | | `onOpenOnlineForm` | None | Fired when the pre-chat form is displayed. | | `onOpenChat` | None | Fired when the chat window opens and the customer enters a queue or starts chatting. | | `onOpenOfflineForm` | None | Fired when the offline form is displayed (when no agents are available). | | `onSkipQueue` | None | Fired when the skip queue option is used. | | `onCustomerInfoSent` | None | Fired when customer information (set via `setCustomerInfo`) has been transmitted to the server. | | `onSessionCreated` | None | Fired when a chat session is successfully created on the server. | | `onSessionEnd` | None | Fired when the chat session ends on the server. | | `onSessionError` | None | Fired when the chat session encounters an error. | ### Combining with other API methods The `setProxy()` method returns the `chatApp` instance, so it can be chained with other methods ```javascript function fn(chatApp) { chatApp .setCustomerInfo({ "First name": "James", "Email address": "james@example.com" }) .setProxy({ onAppStart: function () { console.log('Chat is ready'); }, onAppEnd: function (endChatReason) { if (endChatReason === 'chat-api') { // Chat was ended programmatically via endChat() console.log('Chat ended by application'); } else { // Chat was ended by the customer clicking the close button console.log('Chat ended by user'); } } }); } ``` ### Important Notes * Callbacks are registered once and remain active for the entire chat session lifecycle. * You only need to provide the callbacks you want to use. Any missing callbacks are ignored. * The `onAppEnd` callback fires regardless of how the chat was ended (by the user or via the API). Use the `endChatReason` parameter to differentiate. * The `setProxy()` method should be called during initialization (inside the callback function), before the chat starts. --- ## Getting Started with 8x8 Chat Chapi is what we call our Chat API. It allows you to send and fetch messages from an 8x8 Work chat room. ## Getting Prepared for Beta Access > 🚧 **Get your API Key (beta) — Skip this if you've already got one!** > > There's one prerequisite before you're able to use these APIs, and that's getting your API Key. You can generate your API key in the Admin Console. [Here's how](/actions-events/docs/chat-api-key) > > ## 1. Get Messages from a Room First up, let's get the messages for a room If we're using a `test_key` we'll default our room to the room named `CHAPI sandbox` ```bash curl --request GET \ --url 'https://api.8x8.com/chat/api/chat/v1/messages?pageSize=10' \ --header 'x-api-key: test_key_kjdfidj238jf9123df221' ``` > 📘 **Did you get an error?** > > If you see `{"fault":{"faultstring":"Invalid access token","detail":{"errorcode":"oauth.v2.InvalidAccessToken"}}}` You'll want to make sure you replace the api key (`test_key_kjdfidj238jf9123df221`) with your own. > > Awesome! You should have received a JSON payload with something like the following: ```json [ { "authorUser": { "avatarUrl": "https://s.gravatar.com/avatar/c0fc68541276afaf1ecf7e7f761f518e?s=80", "email": "user@example.com", "id": "007", "name": "Matt Gardner" }, "parsed": "Hello World!", "id": "uv4y2dmXRy_v868BZQJadWAPWv7Am-oRO1p86k00dZY", "timestamp": 1594169304503487 }, { "authorUser": { "avatarUrl": "https://s.gravatar.com/avatar/c0fc68541276afaf1ecf7e7f761f518e?s=80", "email": "user@example.com", "id": "007", "name": "Matt Gardner" }, "parsed": "i r developer!", "id": "AFsG2zb5ckapn-RJalcNWHxZGy70IDhv6dzbkvA5t2g", "timestamp": 1594169027435340 } ] ``` **Two messages already exist in this room — you may not have any. That's ok, let's send a message!** ## 2. Sending a Message to a Room Let's send a message to a room. Copy/paste the below into your terminal ```bash curl -H "Accept: application/json" \ -H 'content-type: application/json' \ -H "x-api-key: test_key_kjdfidj238jf9123df221" \ --request POST \ --data '{"messageRaw":"Hello from Terminal!"}' \ https://api.8x8.com/chat/api/chat/v1/messages ``` **P.S.** Want to see this message come through in realtime? Log into 8x8 Work and find the room called `CHAPI Sandbox`.\*\* *If it doesn't exist, it will once you've successfully sent your first message!* Now that we've sent a message, you should also see it when logged into 8x8 Work. Plus, if we re-run the first snippet, we'll see that our message was added to the array it returns, like so: ```json [ { "authorUser": { "avatarUrl": "https://s.gravatar.com/avatar/c0fc68541276afaf1ecf7e7f761f518e?s=80", "email": "user@example.com", "id": "007", "name": "Matt Gardner" }, "parsed": "Hello World!", "id": "uv4y2dmXRy_v868BZQJadWAPWv7Am-oRO1p86k00dZY", "timestamp": 1594169304503487 }, { "authorUser": { "avatarUrl": "https://s.gravatar.com/avatar/c0fc68541276afaf1ecf7e7f761f518e?s=80", "email": "user@example.com", "id": "007", "name": "Matt Gardner" }, "parsed": "i r developer!", "id": "AFsG2zb5ckapn-RJalcNWHxZGy70IDhv6dzbkvA5t2g", "timestamp": 1594169027435340 }, { "authorUser": { "avatarUrl": "https://s.gravatar.com/avatar/c0fc68541276afaf1ecf7e7f761f518e?s=80", "email": "user@example.com", "id": "007", "name": "Matt Gardner" }, "parsed": "Hello from Terminal!", "id": "AFsG2zb5ckapn-RJalcNWHxZGy70IDhv6dzbkvA5t24", "timestamp": 1594169027435321 } ] ``` ***Note: your API key is limited to 5 requests per second.*** --- ## Glossary of theming items ### Global theme properties | Chat Theme Property | Example value | Description | | --- | --- | --- | | chatBorderRadius | 8px 8px 0 0 | Changes the chat rounding | | chatShadow | # A81F1F | Changes the chat shadow | ### Header | Chat Theme Property | Example value | Description | | --- | --- | --- | | headerBackgroundColor | "#C90C90" | Changes the header background colour | | headerBorderWidth | "0 0 2px 0" | Adds the header border | | headerBorderColor | "#CCCCCC" | Adds the header border colour | | headerBorderRadius | 0 0 6px 6px | Adds the rounding to the header | | headerTextColor | # FFFFFF | Changes the text colour in the header | | headerIconColor | # FFFFFF | Changes the icon colour in the header | | headerIconBackgroundColorActive | # 000000 | Changes the icon background colour when it's active | | headerIconBackgroundColorHover | # CCCCCC | Changes the icon background colour when it's hovered | | headerMenuBackgroundColor | # F1F1F1 | Changes the background of the dropdown menu | | headerMenuBorderRadius | 0 0 6px 6px | Changes the rounding of the dropdown menu | | headerMenuBorderColor | # F1F1F1 | Changes the border colour and style of the dropdown menu | | headerMenuBorderWidth | 2px | Changes the border width of the dropdown menu | | headerMenuShadow | # 9B3636 | Changes the shadow of the dropdown menu | | headerMenuLinkIconColor | # AE2525 | Changes the icon colour in the dropdown menu item | | headerMenuLinkTextColor | # 292929 | Changes the text colour of the dropdown menu item | | headerMenuLinkTextColorFocus | # 292929 | Changes the text colour of the dropdown menu item when it's focused | | headerMenuLinkTextColorHover | # 292929 | Changes the text colour of the dropdown menu item when it's hovered | | headerMenuLinkBackgroundColor | # F9f5EF | Changes the text background colour of the dropdown menu item | | headerMenuLinkBackgroundColorFocus | # D7B7B7 | Changes the text background colour of the dropdown menu item when it's focused | | headerMenuLinkBackgroundColorHover | # E0E0E0 | Changes the text background colour of the dropdown menu item when it's hovered | ### Messages Note, when when the message type shows as INCOMING, this is a message, that comes into the webchat, so sent by the agent or a chatbot. When the message shows as OUTGOING, this is a message that is sent by the person using the webchat, into an agent. | Chat Theme Property | Example value | Description | | --- | --- | --- | | messageIncomingBorderRadius | 0 0 6px 6px | Changes the shape of the incoming messages | | messageIncomingBackgroundColor | # 000000 | Changes the background colour of the incoming messages | | messageIncomingTextColor | # 000000 | Changes the text colour of the incoming messages | | messageIncomingLinkColor | # 000000 | Changes link colour | | messageIncomingLinkColorHover | # 000000 | Changes link colour when it's hovered | | messageIncomingLinkColorFocus | # 000000 | Changes link colour when it's focused | | messageIncomingLinkColorVisited | # 000000 | Changes link colour when it's already visited | | messageOutgoingBorderRadius | 0 0 6px 6px | Changes the shape of the outgoing messages | | messageOutgoingTextColor | # 000000 | Changes the text colour of the outgoing messages | | messageOutgoingBackgroundColor | # 000000 | Changes the background colour of the outgoing messages | | messageOutgoingLinkColor | # 000000 | Changes link colour | | messageOutgoingLinkColorHover | # 000000 | Changes link colour when it's hovered | | messageOutgoingLinkColorFocus | # 000000 | Changes link colour when it's focused | | messageOutgoingLinkColorVisited | # 000000 | Changes link colour when it's already visited | ### Chat window | Chat Theme Property | Example value | Description | | --- | --- | --- | | chatBackgroundColor | # CCCCCC | Changes the chat background colour | | chatTextColor | # 000000 | Changes the text in Main (Invitation, PreChat, Window) | | chatLoaderColor | # 000000 | Change the loader stroke width | | chatSeparatorBackgroundColor | # F1F1F1 | Changes the background colour of the separator located above the send message field and field on PreChat form step | ### Form elements | Chat Theme Property | Example value | Description | | --- | --- | --- | | formLabelTextColor | # 000000 | Changes the label text color for input, radio, textarea, and select components | | formElementTextColor | # 000000 | Changes the text colour of the form element | | formElementBorderColor | # 000000 | Changes the border colour of the form element | | formElementBorderRadius | # 000000 | Changes the rounding of of the form element | | formElementBackgroundColor | # 000000 | Changes the background colour of the form element | | formElementBackgroundColorHover | # 000000 | Changes the background colour of the select option in dropdown when it's hovered | ### Attachments | Chat Theme Property | Example value | Description | | --- | --- | --- | | attachmentItemColor | # 000000 | Changes the text colour inside the adding attachment widget | | attachmentItemBackgroundColor | # FFFFFF | Changes the background colour inside the adding attachment widget | | attachmentItemBorderRadius | 6px | Changes the adding attachment widget rounding | | attachmentItemBorderColor | # CCCCCC | Changes the adding attachment widget border colour | | attachmentLinkColor | # 2983A4 | Changes the link text colour inside the adding attachment widget | | attachmentLinkColorHover | # 00394B | Changes the link text colour inside the adding attachment widget when it's hover | | attachmentLinkColorFocus | # 0049B7 | Changes the link outline colour inside the adding attachment widget when it's focus | | attachmentIconDownloadColor | # B79F00 | Changes the download icon colour in attachment preview | | attachmentIconDownloadColorHover | # 5B4F00 | Changes the colour of the icon to download on hover | --- ## Introduction Welcome to the XCaaS Actions & Events Developer hub. You'll find comprehensive guides and documentation to help you start working with XCaaS Actions & Events as quickly as possible, as well as support if you get stuck. Let's jump right in! Actions & Events will allow you to interact with in progress sessions and transactions, get real-time streams. You can be up and running pausing a contact center recording, changing the state of an agent, adding records to a campaign or stream events from your contact center as quickly as you can fill our the API request! You will find [Guides](/actions-events/docs) which describe the APIs and some use cases and [API References](/actions-events/reference) which allow you to test and try the APIs right from this site. --- ## Invitation quick replies ## Introduction When using the chat invitation, you can configure quick reply suggestions that are displayed to the customer. These are predefined response options that make it easier for customers to start a conversation without typing. The `setInvitationQuickReplies()` method allows you to set these suggestions programmatically via the API. ### Script config Here is an example of how to set quick reply suggestions for the invitation ```javascript function fn(chatApp) { chatApp.setInvitationQuickReplies([ "I need help with my order", "I have a billing question", "I want to speak to an agent" ]); } ``` You can also set them dynamically based on the page the customer is viewing ```javascript function fn(chatApp) { window.chatApp = chatApp; } // On a product page window.chatApp.setInvitationQuickReplies([ "Tell me more about this product", "Is this item in stock?", "I need help choosing" ]); ``` ### Important Notes * Pass an array of strings, where each string is a quick reply option displayed to the customer. * Quick replies are shown in the invitation UI before the customer enters the chat. * The method returns the `chatApp` instance, so it can be chained with other methods. --- ## Key Elements Before getting started using the Chat Gateway, we recommend familiarizing yourself with the concepts and chat elements that you will be working with and are referenced in the following diagram: ## Channel The channel is the medium through which the user relays their inquiry within their Contact Center implementation. This can be any of the traditional communication channels such as phone, email, or chat. ## Conversation A conversation is the information that is exchanged between a Contact Center agent and a user. In order to initiate this exchange of messages, you can use the Chat Gateway API to create a conversation and add the user's messages to it. A conversation can support three types of assignments: queue, agent, and script. If you choose the queue type assignment when creating a new conversation, the interaction will be placed in the specified queue and can be taken up by any agent assigned to that particular queue. Once an agent accepts the interaction, the assignment details for that conversation will be changed to agent and its specific identifier. The assigned agent will then be able to read the messages and respond accordingly. For the script assignment type, the purpose when creating a conversation is to initiate the processing of the assigned social script in order to provide quick replies from our own scripting to our web widget. This allows customers to navigate through questions that use the features introduced to the widget via the Chat Gateway. As a result, the web chat script will be forwarded to the social script at any time. It is common practice to use a conversation for each user case and reuse them if the case is reopened. ## Message A message is a statement conveyed from one conversation participant to the other. ## Participant A participant communicates in the conversation or is the one who sends and receives messages. It can be the user, a bot, or an agent ## Routing Option A routing entity is a resource that defines the rules through which a conversation is routed to an agent. The agent can join the conversation and address the user's inquiry. ## Attachment This is the ability to send a file (image/video/document) to an agent and to receive one back ## Webhook A webhook (also called a web callback), is a way for an app to provide another application with real-time information and notification. For the case of a Chat Gateway integration, 8x8 has to notify your application or bot whenever an agent joins the conversation and adds messages. Therefore, 8x8 will call the webhook URL whenever a new message is being added by any participant to a conversation or a system event is triggered (interaction queued/transferred, agent joined/left, etc.) --- ## Streaming API Overview (Legacy) ## General Approach Generally the Streaming API should be considered only when alternate approaches are not capable of meeting the needs. For general analytic, non real-time even type scenarios the [CCA Realtime], [CCA Historical], [CEX Recent Calls] APIs provide a simpler approach. When there is a need for real-time event streams or other advanced use cases SAPI is often the appropriate API. It is well adopted and leveraged both by customers, partners and technology partners at scale. SAPI is intended to be used in a Server to Server or Cloud to Cloud approach and is not intended to be used where a subscription is made from each agents desktop. In cases where there is a need to deliver events to multiple locations/endpoints a central subscription should be made and this should provide an input to a publisher that satisfies the downstream consumers. ## Overview The SAPI enables you to receive streaming event update information for a Contact Center tenant. You will receive notifications for both agent status and provisioning as well as interactions that occur within your tenant. There is no filter capability all events for all event types are delivered. SAPI provides data for real-time events via server push notifications. It uses a Websocket protocol-based event stream and does not use HTTP. Therefore, in order to communicate via WebSocket protocol, you need to maintain a WebSocket connection to the CC platform. ## Accessing the API ### Authentication In order to subscribe to SAPI events, you must first obtain an authentication token that has been issued for your tenant. This token is a single string that combines your username and password. To either obtain an existing or generate a new authentication token: 1. Log into **Contact Center (CC) Configuration Manager**. 2. On the menu, pane go to **Integration** **> API Token** 3. If no tokens are present, click the **New Token** box next to the **Data Request Token** field. ![api token](../images/aa0e29a-api-token.png "api-token.png") 4. Copy the generated **Data Request Token** to your clipboard for authentication use. **Note**: The generated authentication token for your tenant can be used for your SAPI as well as other 8x8 API requests. It does not expire nor have a time limit unless you decide to change it. If you already have a token and you click on the **New Token** button, your existing authentication token becomes invalid **for all use cases**. You should save a copy in a safe storage location for future reference. ### Subscribing to SAPI #### URL Format wss://vcc-`{cluster}`.8x8.com/api/streaming/v`{version}`/clientconnect/subscribe/TenantUpdates-`{tenant}`-`{subscriptionId}`?desiredOutputType=`{outputType}`&tenantId=`{tenantId}`&subsId=`{subscriptionId}` #### Parameters #### Path | Name | Required | Description | Example | | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------- | | cluster | ✓ | Contact Center cluster can be found in the url when accessing CC Configuration Manager. North America starts NAEurope starts EUCanada starts CAAsia Pacific starts APAustralia starts AUBell Canada starts BCSandbox starts SB | eu3 | | version | ✓ | The API version. The current version is 1 resulting in v1 | 1 | | subscriptionId | ✓ | This is a consumer specified unique identifier for the subscription. See [Connection Lifecycle Information](/actions-events/docs/legacy-streaming-api-overview#connection-lifecycle-information) for guidance. | real-time-monitor | #### Query | Name | Required | Description | Example | | ----------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- | | tenantId | ✓ | The CC Tenant name of the tenant to list agent status for. Tenant name is generally the same as the username above. It can be located in CC Configuration Manager @ Home :: Profile :: Tenant Name | acmecorp01 | | subscriptionId | ✓ | As specified in the `Path` above. | real-time-monitor | | desiredOutputType | | This controls the output format of the stream. Options are: JSON, XML, NEWLINE_JSON and NEWLINE_XML. The NEWLINE variants will emit a newline after each event in the specified format | NEWLINE_JSON | ### Connection Lifecycle Information Please review the following details before you begin using SAPI: * Only three SAPI clients per tenant can be connected at a given time. * If you wish to make more than one distinct connection/subscription you MUST use a unique subscriptionId for each connection. (Max 3 concurrent subscriptions) * A subscription is valid for 60 minutes after which it will be disconnected. * On disconnect the subscription will cache up to 2 hours of data, reconnecting using the same subscriptionId will deliver all the cached events. * Consumers should reconnect using the same subscriptionId to continue the stream. * If a consumer subscribes with the same subscriptionId more than 2 hours after disconnecting this is treated as a new subscription and no cached events are delivered. ## Event Reference ### Event types / call state The following table lists the SAPI Event Type with the corresponding Call State ID and definition: > 📘 **callState ID will only be available for Call/Interaction events.** > > Property `callState` is not present in event types indicated by callState ID **`NA`**. > > | **Event Type** | **callState ID** | **Definition** | | ---------------------------------- | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **`AgentStatusChange`** | **`NA`** | Indicates the current status of the monitored agent. | | **`AgentProvChange`** | **`NA`** | Indicates a change to the agent profile. For example; display name change, etc. | | **`InteractionCreated`** | **`CS_IDLE, CS_QUEUED`** | Indicates that a new outbound or inbound phone call has been created. Note that if it is an outbound type, it will be subject to queuing if part of a campaign prior to dialing. | | **`InteractionDeleted`** | **`CS_DISCONNECTED`** | Indicates a phone call hangup or a single call leg when leaving a conference scenario. Also includes the following enums: **`hangupInitiator`**-**`SYSTEM`****`AGENT`****`CUSTOMER`****`callHangupReason`**-**`CEC_NONE`****`CEC_DISCONNECT_NORMAL`****`CEC_DISCONNECT_BUSY`****`CEC_DISCONNECT_BADADDRESS`****`CEC_DISCONNECT_NOANSWER`****`CEC_DISCONNECT_CANCELLED`****`CEC_DISCONNECT_REJECTED`****`CEC_DISCONNECT_FAILED`****`CEC_DISCONNECT_BLOCKED`** | | **`Interaction Hold/Unhold`** | **`CS_HOLD /CS_CONNECTED`** | Indicates the playing of music on hold (MOH) for an ongoing phone call. | | **`InteractionQueued`** | **`CS_QUEUED`** | Indicates an incoming phone call. The newly created inbound phone call has been queued and is waiting to be assigned to an agent. | | **`InteractionAssigned`** | **`CS_IDLE`** | This means an outbound or an inbound call has been assigned to an available agent. | | **`InteractionRecordingStarted`** | **`NA`** | Indicates that the system has started recording a call leg. | | **`InteractionAccepted`** | **`CS_INPROGRESS /CS_CONNECTED`** | Indicates an outbound phone call is in progress and ringing at the destination with an accepted agent waiting for the customer to answer. If inbound, this means the queued phone call has been accepted by an agent | | **`InteractionCustomerAccepted`** | **`CS_CONNECTED`** | Indicates that a ringing outbound phone call has been answered by the destination external participant / customer. | | **`InteractionPostProcess`** | **`CS_DISCONNECTED`** | Indicates the start of a phone call wrap-up for a call that has concluded. | | **`InteractionRecordingReady`** | **`NA`** | Indicates that an interaction recording is ready for analysis. | | **`InteractionEndPostProcess`** | **`CS_DISCONNECTED`** | Indicates the conclusion of a phone call wrap process. | | **`InteractionDeassigned`** | **`CS_DISCONNECTED`** | Indicates the reassigning of a phone call from an available agent. This can occur if the agent has not accepted the offered phone call on preview mode and then the call times out. | | **`InteractionParticipantChange`** | **`CS_DISCONNECTED`** | When two phone lines are joined or when a supervisor joins an ongoing call. | ### Event Sequence Examples This section describes example call flow scenarios with the **`EventTypes`** and **`callStates`** that are encountered. #### Inbound call | **Action** | **EventType** | **callState** | | ------------------------------------------------------------------------------------ | ------------------------------- | --------------------- | | 1. An incoming call from a customer. | **`InteractionCreated`** | **`CS_QUEUED`** | | 2. The call goes into queue and is waiting to be assigned to an agent. | **`InteractionQueued`** | **`CS_QUEUED`** | | 3. The call is assigned to an agent. | **`InteractionAssigned`** | **`CS_QUEUED`** | | 4. The agent accepts the call and speaks with the customer. | **`InteractionAccepted`** | **`CS_CONNECTED`** | | 5. Automatic post processing starts (i.e., the conclusion or wrapping up of a call). | **`InteractionPostProcess`** | **`CS_DISCONNECTED`** | | 6. Automatic post processing concludes. | **`InteractionEndPostProcess`** | **`CS_DISCONNECTED`** | | 7. The customer ends the call. | **`InteractionDeassigned`** | **`CS_DISCONNECTED`** | | 8. The agent ends the call. | **`InteractionDeassigned`** | **`CS_DISCONNECTED`** | #### Outbound call without queue | **Action** | **EventType** | **callState** | | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | --------------------- | | 1. An incoming call from a customer. | **`InteractionCreated`** | **`CS_IDLE`** | | 2. The call is assigned to an agent. | **`InteractionAssigned`** | **`CS_IDLE`** | | 3. The system has started recording a call leg. | **`InteractionRecordingStarted`** | **`NA`** | | 4. The outbound phone call is in progress and ringing at the destination with an accepted agent waiting for the customer to answer. | **`InteractionAccepted`** | **`CS_INPROGRESS`** | | 5. The outbound phone call has been answered by the destination external participant / customer. | **`InteractionCustomerAccepted`** | **`CS_CONNECTED`** | | 6. The conclusion of the phone call wrap up process. | **`InteractionEndPostProcess`** | **`CS_DISCONNECTED`** | | 7. The phone call has been reassigned from an agent. | **`InteractionDeAssigned`** | **`CS_DISCONNECTED`** | #### Outbound call with queue | **Action** | **EventType** | **callState** | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | --------------------- | | 1. An outbound phone call has been created and is a queue. | **`InteractionCreated`** | **`CS_IDLE`** | | 2. The outbound call has been assigned to an available agent. | **`InteractionAssigned`** | **`CS_IDLE`** | | 3. The system has started call leg recording. | **`InteractionRecordingStarted`** | **`NA`** | | 4. The outbound phone call is in progress and ringing at the destination with an accepted agent waiting for the customer to answer. | **`InteractionAccepted`** | **`CS_INPROGRESS`** | | 5. The outbound phone call has been answered by the intended destination external participant / customer. | **`InteractionCustomerAccepted`** | **`CS_CONNECTED`** | | 6. The start of call wrap-up for the concluded phone call. | **`InteractionEndPostProcess`** | **`CS_DISCONNECTED`** | | 7. The phone call has been reassigned from an available agent. This can occur if the agent has not accepted the offered phone call on preview mode and then the call times out. | **`InteractionDeAssigned`** | **`CS_DISCONNECTED`** | #### Outbound conference call (with queue) | **Action** | **EventType** | **CallState** | | -------------------------------------------------------------------------------------------------- | ---------------------------------- | --------------------------- | | 1. An outbound phone call has been created by an agent and is in the queue. | **`InteractionCreated`** | **`CS_IDLE`** | | 2. The agent’s phone rings. | **`InteractionQueued`** | **`CS_IDLE`** | | 3. The agent picks up the incoming call. | **`InteractionAssigned`** | **`CS_IDLE`** | | 4. The customer’s phone rings. | **`InteractionAccepted`** | **`CS_INPROGRESS`** | | 5. The customer picks up the call. | **`InteractionCustomerAccepted`** | **`CS_CONNECTED`** | | 6. The initial first agent calls a second agent on line 2 (from the agent tab on the line 1 call). | **`InteractionCreated`** | **`CS_IDLE`** | | 7. The second agent’s phone starts ringing. | **`InteractionAssigned`** | **`CS_IDLE`** | | 8. The second agent picks up the call. | **`InteractionAccepted`** | **`CS_INPROGRESS`** | | 9. The second agent is connected on the Agent Workspace | **`InteractionAssigned`** | **`CS_INPROGRESS`** | | 10. The first agent and the second agent are connected on line 2. | **`InteractionAccepted`** | **`CS_CONNECTED`** | | 11. The first agent joins the calls together on line 1. | **`InteractionEndPostProcess`** | **`CS_DISCONNECTED`** | | 12. Line 2 is de-assigned | **`InteractionDeassigned`** | **`CS_DISCONNECTED`** | | 13. All calls are joined together. | **`InteractionReconnect`** | **`CS_DISCONNECTED`** | | 14. All calls are joined together. | **`InteractionParticipantChange`** | **`CS_CONNECTED`** | | 15. Line 2 is deassigned | **`InteractionDeassigned`** | **`CS_DISCONNECTED`** | | 16. The system disconnects line 2 `"hangupInitiator": "SYSTEM"` | **`InteractionDeleted`** | **`CEC_DISCONNECT_NORMAL`** | | 17. A conference call is created. | **`InteractionJoinLinesRequest`** | **`NA`** | | 18. The first agent leaves the created conference call. | **`InteractionPostProcess`** | **`CS_DISCONNECTED`** | | 19. All calls are disconnected | **`InteractionParticipantChange`** | **`CS_DISCONNECTED`** | | 20. The conclusion of the phone call wrap up process. | **`InteractionEndPostProcess`** | **`CS_DISCONNECTED`** | | 21. The phone call has been reassigned from an agent. | **`InteractionDeassigned`** | **`CS_DISCONNECTED`** | | 22. The second agent leaves the call | **`InteractionPostProcess`** | **`CS_DISCONNECTED`** | | 23. The conclusion of the phone call wrap up process. | **`InteractionEndPostProcess`** | **`CS_DISCONNECTED`** | | 24. The phone call has been reassigned from an agent. | **`InteractionDeassigned`** | **`CS_DISCONNECTED`** | | 25.The agent leaves the conference call scenario. `"hangupInitiator": "AGENT"` | **`InteractionDeleted`** | **`CEC_DISCONNECT_NORMAL`** | ### Transfer of call to another agent (inbound or outbound call) | **Action** | **EventType** | **callState** | | ----------------------------------------------------------------------------------------- | ---------------------------------- | --------------------- | | 1. An outbound phone call has been created by the first agent and is subject to queuing. | **`InteractionCreated`** | **`CS_IDLE`** | | 2. The agent’s phone starts ringing. | **`InteractionQueued`** | **`CS_IDLE`** | | 3. The agent picks up the phone call. | **`InteractionAssigned`** | **`CS_IDLE`** | | 4. The customer’s phone starts ringing. | **`InteractionAccepted`** | **`CS_INPROGRESS`** | | 5. The customer picks up the call. | **`InteractionCustomerAccepted`** | **`CS_CONNECTED`** | | 6. The first agent calls another agent on line 2 (from the agent tab on the line 1 call). | **`InteractionCreated`** | **`CS_IDLE`** | | 7. The second agent’s phone starts ringing. | **`InteractionAssigned`** | **`CS_IDLE`** | | 8. The second agent picks up the phone call. | **`InteractionAccepted`** | **`CS_INPROGRESS`** | | 9. The second agent is connected on the AGUI. | **`InteractionAssigned`** | **`CS_INPROGRESS`** | | 10. The first agent and the second agent are connected on line 2. | **`InteractionAccepted`** | **`CS_CONNECTED`** | | 11. The first agent transfers the call to the second agent. | **`InteractionTransferRequest`** | **`NA`** | | 12.The start of call wrap-up for the concluded phone call. | **`InteractionPostProcess`** | **`CS_DISCONNECTED`** | | 13. Another agent has joined the ongoing call. | **`InteractionParticipantChange`** | **`CS_CONNECTED`** | | 14. The phone call wrap up process has concluded. | **`InteractionEndPostProcess`** | **`CS_DISCONNECTED`** | | 15. The phone call has been reassigned from the answering agent. | **`InteractionDeassigned`** | **`CS_DISCONNECTED`** | | 16. The start of phone call wrap-up. | **`InteractionPostProcess`** | **`CS_DISCONNECTED`** | | 17. All of the separate calls have been reconnected. | **`InteractionReconnect`** | **`CS_DISCONNECTED`** | | 18. Another agent has joined the ongoing call. | **`InteractionParticipantChange`** | **`CS_DISCONNECTED`** | | 19. The re-assignment of the phone call from an available agent. | **`InteractionDeassigned`** | **`CS_DISCONNECTED`** | | 20. The phone interaction has been deleted. | **`InteractionDeleted`** | **`CS_DISCONNECTED`** | | 21. The customer ends the call with the second agent. | **`InteractionEndPostProcess`** | **`CS_DISCONNECTED`** | | 22. The phone call has been reassigned from an available agent. | **`InteractionDeassigned`** | **`CS_DISCONNECTED`** | | 23. The phone calls are joined in the ongoing call. | **`InteractionParticipantChange`** | **`CS_DISCONNECTED`** | | 24. The start of phone call wrap-up for the concluded call. | **`InteractionPostProcess`** | **`CS_DISCONNECTED`** | | 25. The reassigning of a phone call from an available agent. | **`InteractionDeassigned`** | **`CS_DISCONNECTED`** | | 26. The phone call has concluded. | **`InteractionDeleted`** | **`CUSTOMER`** | ### Transfer of a call to an inbound queue | **Action** | **EventType** | **callState** | | ---------------------------------------------------------------------------------------- | --------------------------------- | --------------------- | | 1. An outbound phone call has been created by the first agent and is subject to queuing. | **`InteractionCreated`** | **`CS_IDLE`** | | 2. The agent’s phone starts ringing. | **`InteractionQueued`** | **`CS_IDLE`** | | 3. The agent picks up the phone call. | **`InteractionAssigned`** | **`CS_IDLE`** | | 4. The customer’s phone starts ringing. | **`InteractionAccepted`** | **`CS_INPROGRESS`** | | 5. The customer picks up the call. | **`InteractionCustomerAccepted`** | **`CS_CONNECTED`** | | 6. The agent transfers the call to an inbound queue. | **`InteractionPostProcess`** | **`CS_DISCONNECTED`** | | 7. A new interaction is created. | **`InteractionCreated`** | **`CS_IDLE`** | | 8. The customer is queuing in the Inbound queue. | **`InteractionQueued`** | **`CS_IDLE`** | | 9. The agent call on line 1 has concluded. | **`InteractionEndPostProcess`** | **`CS_DISCONNECTED`** | | 10. The call has been deassigned. | **`InteractionDeassigned`** | **`CS_DISCONNECTED`** | | 11. The phone call has concluded. `"hangupInitiator": "AGENT"` | **`InteractionDeleted`** | **`CS_DISCONNECTED`** | ## Samples and Test Tool ### The SAPI Sample client A sample client written in Java is available. This is provided as is as an example of how to consume the SAPI API. You can receive SAPI event notifications by installing and using the SAPI sample client. The SAPI sample client downloadable [.zip](https://github.com/8x8/files/raw/master/vcc-sapiclient.zip) is provided as a reference implementation only. Please validate this code meets your requirements before using it. The SAPI sample client is a Java-based command-line utility that facilitates connection to the SAPI endpoint. **Note:** SAPI Status codes are displayed as numbers that correspond to the manually selected status code. Agent status codes do not come up with the same status ID number in SAPI if you have more than one status code list. For example, the status code for when one agent is on lunch break may be represented as a different lunch break status code in another different list on the same tenant. ### Browser Test Tool In the SAPI web client, the SAPI query URL in the browser field is constructed as follows: `[https://vcc-{ccPlatform}.8x8.com/api/streaming/v1/TenantUpdates.jsp](https://vcc-{ccPlatform}.8x8.com/api/streaming/v1/TenantUpdates.jsp)` Revise your query URL based on the login URL of your Tenant. (Refer to the [Platform URL Guide](https://support.8x8.com/@api/deki/files/2500/Platform-URL-Guide-Virtual-Contact-Center.pdf?revision=4) to retrieve your login URL) To access your tenant: 1. Click the **Subscribe** button. The authentication window displays: ![tenant updates](../images/1a8c4c0-tenant-updates.png "tenant-updates.png") 2. Enter your tenant **User Name** and your **Authentication Token** in the **Password** field and click **OK**. Following successful connection with the Tenant, the **Tenant Updates** screen displays: ![tenant-update](../images/2104f74-tenant-update-2.png "tenant-update-2.png") The displayed **Tenant Updates** screen consists of the following information fields: * **Subscription ID** - the unique ID for the SAPI connection from the Tenant. Each tenant manages its own subscription ID. Since the SAPI stores messages for a certain period of time (even if the client is disconnected), using the same, consistently named subscription ID is important in order to receive stored messages. * **Tenant ID** - your 8x8 Tenant ID. * **Desired Output Type** - can be in either XML or JSON formats. * **Subscribe** - initiates a subscription to events for the next 60 minutes. * **Unsubscribe** - disconnects the client from the SAPI server and starts storing messages for possible reconnection. #### Sample Events #### Heartbeat ```json { "ClientCheck": { "heartbeat": "[HEARTBEAT]", "msgInfo": { "instanceId": "us1tomcat04.us1.whitepj.net-sapi-v1", "sequenceId": 70, "timestamp": 1669230226093 }, "subscription-id": "real-time-monitor", "tenant-id": "acmecorp01" } } ``` #### Agent Status Change ```json { "AgentStatusChange": { "agentId": "ag64oyEUb_Sk6bxVB9P5yaaa", "msgInfo": { "instanceId": "us1tomcat04.us1.whitepj.net-sapi-v1", "sequenceId": 215, "timestamp": 1669235544917 }, "newReasonCodeUser": "801=1722", "newState": 5, "newSubState": "none", "newSubStateReason": "none", "statusEventTS": 1669235544 } } ``` #### Agent Provisioning Change ```json { "AgentProvChange": { "agentId": "ag64oyEUb_Sk6bxVB9P5yaaa", "enabled": "yes", "event": "AgentSkillChanged", "msgInfo": { "instanceId": "us1tomcat04.us1.whitepj.net-sapi-v1", "sequenceId": 219, "timestamp": 1669235693377 }, "skillId": "acmecorp01-ag64oyEUb_Sk6bxVB9P5yaaa-email-3086-063bf4a0-8eab-465c-b03a-09c3e0daf710", "tenantSkillId": "acmecorp01-email-3086-de6fd357-76b1-4587-ab7b-86b31357f56c" } } ``` #### Interaction :: InteractionCreated ```json { "Interaction": { "attachedData": { "attachedDatum": [{ "attachedDataKey": "@pri", "attachedDataValue": 100 }, { "attachedDataKey": "callingName", "attachedDataValue": "Andrew Cunningh" }, { "attachedDataKey": "cha", "attachedDataValue": 13125555068 }, { "attachedDataKey": "cnt", "attachedDataValue": 0 }, { "attachedDataKey": "con", "attachedDataValue": 0 }, { "attachedDataKey": "med", "attachedDataValue": "T" }, { "attachedDataKey": "pho", "attachedDataValue": 5515557212 }, { "attachedDataKey": "phoneNum", "attachedDataValue": 5515557212 }, { "attachedDataKey": "remoteCallingName", "attachedDataValue": "Andrew Cunningh" }, { "attachedDataKey": "remotePhoneNum", "attachedDataValue": "+15515557212" }, { "attachedDataKey": "tok", "attachedDataValue": 19727 } ] }, "callState": "CS_QUEUED", "event": "InteractionCreated", "inboundChannelid": 13125555068, "interactionEventTS": 1669235827, "interactionGUID": "int-184a63564dc-ohWfVIbHJz2Hr2JFhAfdlb4Fa-phone-00-acmecorp01", "msgInfo": { "instanceId": "us1tomcat04.us1.whitepj.net-sapi-v1", "sequenceId": 224, "timestamp": 1669235827942 }, "resourceType": 0 } } ``` #### Interaction :: InteractionQueued ```json { "Interaction": { "attachedData": { "attachedDatum": [{ "attachedDataKey": "@pri", "attachedDataValue": 100 }, { "attachedDataKey": "callingName", "attachedDataValue": "Andrew Cunningh" }, { "attachedDataKey": "cha", "attachedDataValue": 13125555068 }, { "attachedDataKey": "channelName", "attachedDataValue": "Acme Ads OG" }, { "attachedDataKey": "cnt", "attachedDataValue": 0 }, { "attachedDataKey": "con", "attachedDataValue": 0 }, { "attachedDataKey": "med", "attachedDataValue": "T" }, { "attachedDataKey": "otim", "attachedDataValue": 1669235836 }, { "attachedDataKey": "pcsOffered", "attachedDataValue": "no" }, { "attachedDataKey": "pho", "attachedDataValue": 5515557212 }, { "attachedDataKey": "phoneNum", "attachedDataValue": 5515557212 }, { "attachedDataKey": "priority", "attachedDataValue": 50 }, { "attachedDataKey": "que", "attachedDataValue": "acmecorp01~~queue~~phone~~591" }, { "attachedDataKey": "queueDirection", "attachedDataValue": "in" }, { "attachedDataKey": "remoteCallingName", "attachedDataValue": "Andrew Cunningh" }, { "attachedDataKey": "remotePhoneNum", "attachedDataValue": "+15515557212" }, { "attachedDataKey": "tenantName", "attachedDataValue": "acmecorp01" }, { "attachedDataKey": "tenantRecServer", "attachedDataValue": "na12nfs01" }, { "attachedDataKey": "tenantSkillName", "attachedDataValue": "Test Sales" }, { "attachedDataKey": "tim", "attachedDataValue": 1669235836 }, { "attachedDataKey": "tok", "attachedDataValue": 19727 } ] }, "callState": "CS_QUEUED", "direction": "in", "event": "InteractionQueued", "eventTS": 1669235836, "inboundChannelid": 13125555068, "interactionEventTS": 1669235836, "interactionGUID": "int-184a63564dc-ohWfVIbHJz2Hr2JFhAfdlb4Fa-phone-00-acmecorp01", "isAgentInitiated": false, "mediaType": "phone", "msgInfo": { "instanceId": "us1tomcat04.us1.whitepj.net-sapi-v1", "sequenceId": 225, "timestamp": 1669235836708 }, "priority": 50, "queueId": 591, "queueList": 591, "queueTime": 1669235836, "resourceType": 0, "transactionNum": 19727 } } ``` #### Interaction :: InteractionDeleted Incoming call ended by caller while in IVR ```json { "Interaction": { "attachedData": { "attachedDatum": [{ "attachedDataKey": "@pri", "attachedDataValue": 100 }, { "attachedDataKey": "callingName", "attachedDataValue": "Andrew Cunningh" }, { "attachedDataKey": "cha", "attachedDataValue": 13125555068 }, { "attachedDataKey": "cnt", "attachedDataValue": 0 }, { "attachedDataKey": "con", "attachedDataValue": 0 }, { "attachedDataKey": "med", "attachedDataValue": "T" }, { "attachedDataKey": "pcsOffered", "attachedDataValue": "no" }, { "attachedDataKey": "pho", "attachedDataValue": 5515557212 }, { "attachedDataKey": "phoneNum", "attachedDataValue": 5515557212 }, { "attachedDataKey": "remoteCallingName", "attachedDataValue": "Andrew Cunningh" }, { "attachedDataKey": "remotePhoneNum", "attachedDataValue": "+15515557212" }, { "attachedDataKey": "tok", "attachedDataValue": 19738 } ] }, "callHangupReason": "CEC_DISCONNECT_NORMAL", "callState": "CS_DISCONNECTED", "dispositionCode": 1000, "event": "InteractionDeleted", "hangupInitiator": "CUSTOMER", "inboundChannelid": 13125555068, "interactionEventTS": 1669236911, "interactionGUID": "int-184a645e074-zGIsMxOkCYzQ8yg68e3t61i7A-phone-00-acmecorp01", "isAgentInitiated": false, "isDirectAccess": false, "isQueued": false, "mediaType": "phone", "msgInfo": { "instanceId": "us1tomcat04.us1.whitepj.net-sapi-v1", "sequenceId": 251, "timestamp": 1669236911188 }, "recordingMode": "no", "rejectReason": 0, "resourceType": 0 } } ``` --- ## Notification sound ## Introduction By default, the webchat widget plays a sound when a new message arrives. Using the `setNotificationSoundToggle()` method, you can control whether this notification sound is enabled or disabled. This is useful when your website has its own notification system, or when you want to give customers a quieter experience. ### Script config Here is an example of how to disable the notification sound during initialization ```javascript function fn(chatApp) { chatApp.setNotificationSoundToggle(false); } ``` You can also toggle it dynamically based on user preference ```javascript function fn(chatApp) { window.chatApp = chatApp; } document.getElementById('mute-toggle').addEventListener('change', function (e) { window.chatApp.setNotificationSoundToggle(e.target.checked); }); ``` ### Important Notes * Pass `true` to enable notification sounds or `false` to disable them. * This preference is persisted for the duration of the chat session. * The method returns the `chatApp` instance, so it can be chained with other methods. --- ## Reset customer information ## Introduction If you have previously set customer information using `setCustomerInfo()`, you may need to clear it. For example, when a different customer logs into your website during the same browser session, or when you want to ensure no stale data is carried over into a new chat. The `resetCustomerInfo()` method clears all previously set customer information. ### Script config Here is an example of how to reset the customer information ```javascript function fn(chatApp) { window.chatApp = chatApp; } // Set initial customer info window.chatApp.setCustomerInfo({ "First name": "James", "Email address": "james@example.com" }); // Later, clear it when the user logs out window.chatApp.resetCustomerInfo(); ``` ### Important Notes * After calling `resetCustomerInfo()`, any new chat session will not include the previously set customer data. * You can call `setCustomerInfo()` again after resetting to provide new data. * The method returns the `chatApp` instance, so it can be chained with other methods. --- ## Set chat variables ## Introduction Variables can be sent into the widget for a number of reasons. The first is so they can be used to update data in the 8x8 Native CRM, for example sending in an email address create a record and execute a screen pop. Also you can set custom variables, which can then be used for routed within the 8x8 script. CRM variables: '$language', '$caseNumber', '$accountNumber', '$emailAddress', '$emailSubject', '$emailBody' ### Script config Here is an example, where we are setting a variable to show the customer is a VIP, so inside the scripting they can go to that queue, and also setting the email address to it can create a screen pop for the agent. ```javascript // set variables chatApp.setVariables({"_VIP":"YES", "$language":"fr", "$emailAddress": "testemail@example.com"} ``` To add this, you need to add it at the bottom of the script in this area here ```html })( --ADD THE FUNCTION CHATAPP CODE HERE ); ``` Then, the full script will look like this ```javascript ``` ### 8x8 script Then inside the 8x8 script, you can use the variable like the below, to route to the queue you want them to go to ![image](../images/e5673df5d46fcce77ca354d0aec357279f965a271178f90a8d752f9fa9572392-VIP.png "Chat Gateway flow.jpg") ### Important Notes * Language $language is the only CRM variable that will not be sent back in CM to use inside chat script testVariable node but will actually take effect immediately and change the chat language; also it will set the default language option value in pre-chat language question drop-down select (if pre-chat and language question set). * If no pre-chat form/no language question is set then the only effect is the language change for the available next steps (chat window, offline form). * All custom variables prefixed with _ and the rest of the CRM variables available ($emailAddress, $emailBody etc.) end up in the interaction details and additional properties in AW. * The variables key:value pair is limited to 255 characters for the key and the value each. Any key:pair bigger than the specified value will be ignored. * The variable key that is not a string and does not match the pattern *(any alphanumeric characters plus underscore _, any digit 1-9, a hyphen -)* will be ignored and a warning is shown in the console. ### Troubleshooting * Make sure that, when passing the variables the correct format and type string is adhered to and it is a key-value object. * If the message *"Customer variable: provided variable [variableKey] does not match the expected pattern or its chars exceeded the max length of 255. It will be discarded"*, it means that the set custom variable key value is of a format or pattern unsupported by the embedded chat. Only custom variables prefixed with _ and that match the pattern *(any alphanumeric characters plus underscore _, any digit 1-9, a hyphen -)* are accepted. * If the message *"Customer variable: provided system variable [variableKey ] it's not supported. It will be discarded"*, it means that the set system variable key value is of a format or pattern unsupported by the embedded chat. Only CRM variables prefixed with $ and that match the pattern *(any alphanumeric characters plus underscore _, any digit 1-9, a hyphen -)* and are included in the System Variables map are accepted. * If the message *"Customer variable: provided variable [variableKey] is not a string or its chars exceeded the max length of 255. It will be discarded"*, it means that the set variable key value is of a format or size unsupported by the embedded chat. Only variables of format string with key and value max length of 255 characters each are accepted. --- ## Authentication(Streaming) ## Overview The 8x8 Event Streaming service supports two types of authentication credentials, depending on how you obtain them and which 8x8 system you're using. > 📘 **Authentication Method** > > Both authentication types use the same `X-API-Key` HTTP header, making the authentication mechanism consistent across both methods. > > ## Authentication Types ### Admin Console Keys (X-API-Key Header) > ✅ **Recommended** > > Admin Console Keys are the **recommended authentication method** for production applications. They provide centralized credential management through the 8x8 Admin Console. > > **Characteristics:** - Obtained from **8x8 Admin Console** at [admin.8x8.com](https://admin.8x8.com) - Key format: Always starts with `eght_` - Authentication method: HTTP header `X-API-Key` **How to Obtain:** If you do not have the API Keys option in Admin Console, you do not have the correct permission/role. ![Admin Console API Keys Location](../../../analytics/images/API_Key_Generation.png) 1. Log into **[8x8 Admin Console](https://admin.8x8.com)** 2. In the **SETUP** section, click **API Keys** ![API Keys Dashboard](../../../analytics/images/API_Key_List.png) 3. Click **"Create App"** 4. Enter an application name (no spaces allowed) 5. In the **API Products** dropdown, select **"Pulsar Event Stream"** ![Create App Dialog - Pulsar Event Stream](../../images/admin-console-create-app.png) 6. Click **"Save"** to generate your key The dashboard will display your newly created app with the generated API key: ![Generated API Key - Pulsar Event Stream](../../images/admin-console-generated-key.png) **Note:** The key starts with `eght_` and can be viewed anytime by clicking the eye icon. **Required Permission:** **Application Credentials** permission (**Company Admin Role** or custom role) **Documentation:** [How to get API Keys](../../../analytics/docs/how-to-get-api-keys) **Usage:** ```text X-API-Key: eght_your_key_here ``` ### Contact Center (CC) Tokens (X-API-Key Header) **Characteristics:** - Obtained from **Contact Center Configuration Manager** - Token format: Base64-encoded `username:api-password` (does not start with `eght_`) - Authentication method: HTTP header `X-API-Key` (same as Admin Console Keys) **How to Obtain:** 1. Log into **Contact Center Configuration Manager** 2. Navigate to **Integration** > **API Token** 3. If you already have a **Data Request Token** displayed, copy that token 4. If no token exists, click **"New Token"** to generate one, then copy it **Important Notes:** - The token combines your **username** and **API password** - Token does not expire unless you generate a new one - Generating a new token invalidates the previous token for all use cases **Documentation:** [Streaming API Overview](../legacy-streaming-api-overview) **Usage:** ```text X-API-Key: your_cc_token_here ``` > 📘 **Backward Compatibility** > > CC Tokens also continue to work with the legacy `Authorization: Basic` header and `?token=Basic+` URL parameter for backward compatibility with existing clients. > > > 📘 **Authentication Methods: Header vs Query Parameter** > > The API key can be provided in two ways: > > **HTTP Header (Recommended):** > - `X-API-Key: your-key-here` > - Header names are case-insensitive per HTTP spec (X-API-Key, x-api-key, X-Api-Key all work) > - More secure (not logged in URLs) > > **Query Parameter:** > - `?x-api-key=your-key-here` > - Parameter name is case-sensitive (must be lowercase `x-api-key`) > - Useful when headers cannot be set (e.g., some browser contexts) > > ## Example Usage import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ### Using Admin Console Keys (X-API-Key Header) > ✅ **Recommended Method** > > Admin Console Keys are the recommended authentication method for production applications. > > ```go headers := http.Header{} headers.Set("X-API-Key", "eght_your_admin_console_key") dialer := websocket.Dialer{ HandshakeTimeout: 45 * time.Second, TLSClientConfig: &tls.Config{ InsecureSkipVerify: false, // Set to true only for testing }, } conn, resp, err := dialer.Dial(wsURL, headers) ``` ```java String xApiKey = "eght_your_admin_console_key"; PulsarWebSocketClient client = new PulsarWebSocketClient(serverUri, xApiKey); // The constructor automatically adds the header: if (xApiKey != null && !xApiKey.isEmpty()) { addHeader("X-API-Key", xApiKey); } ``` ```javascript const WebSocket = require('ws'); const wsURL = 'wss://your-host/ws/v2/reader/persistent/tenant/namespace/topic'; const ws = new WebSocket(wsURL, { headers: { 'X-API-Key': 'eght_your_admin_console_key' } }); ``` ### Using CC Tokens (X-API-Key Header) > 📘 **Alternative Method** > > CC Tokens remain supported for existing integrations or when Admin Console Keys are not available. Use the `X-API-Key` header (same format as Admin Console Keys). > > ```go headers := http.Header{} headers.Set("X-API-Key", "your_cc_token_here") dialer := websocket.Dialer{ HandshakeTimeout: 45 * time.Second, TLSClientConfig: &tls.Config{ InsecureSkipVerify: false, // Set to true only for testing }, } wsURL := "wss://your-host/ws/v2/reader/persistent/tenant/namespace/topic" conn, resp, err := dialer.Dial(wsURL, headers) ``` ```java String ccToken = "your_cc_token_here"; String wsURL = "wss://your-host/ws/v2/reader/persistent/tenant/namespace/topic"; // Create client with X-API-Key header PulsarWebSocketClient client = new PulsarWebSocketClient(URI.create(wsURL)); client.addHeader("X-API-Key", ccToken); ``` ```javascript const WebSocket = require('ws'); const wsURL = 'wss://your-host/ws/v2/reader/persistent/tenant/namespace/topic'; const ws = new WebSocket(wsURL, { headers: { 'X-API-Key': 'your_cc_token_here' } }); ``` ```python import websocket ws_url = "wss://your-host/ws/v2/reader/persistent/tenant/namespace/topic" headers = { "X-API-Key": "your_cc_token_here" } ws = websocket.WebSocketApp(ws_url, header=headers) ``` ```javascript // Browsers cannot set custom headers for WebSockets // Use URL parameter instead const wsURL = 'wss://your-host/ws/v2/reader/persistent/tenant/namespace/topic?x-api-key=your_cc_token_here'; const ws = new WebSocket(wsURL); // Note: Token will be visible in browser DevTools and history ``` ## Troubleshooting Authentication ### Common Issues **401 Unauthorized** - Verify your credential is correct and complete - For Admin Console Keys: Ensure the key starts with `eght_` - For CC Tokens: Ensure the token is the full base64-encoded string from Configuration Manager - Check that you're using the right tenant name - Ensure you're using the `X-API-Key` header (or `?x-api-key=` URL parameter for browsers) ## Next Steps - [Connection Guide](./connection.md) - Learn about establishing connections - [Message Format](./message-format.mdx) - Understand event structure - [Code Examples](./examples/golang.md) - See authentication in action --- ## Connection Guide Connect to the 8x8 Event Streaming service using WebSocket. ## WebSocket URL Format The WebSocket connection URL follows this structure: ```text wss://{host}/ws/v2/reader/persistent/{tenant}/{namespace}/{topic} ``` ### URL Components | Component | Description | Example | |-------------|---------------------------------------------------------------------------------|--------------------------| | `host` | WebSocket proxy server hostname (see [Regional Endpoints](#regional-endpoints)) | `pulsar-ws-euw2.8x8.com` | | `tenant` | Your 8x8 tenant name | `my-tenant` | | `namespace` | Pulsar namespace (typically `event-v1`) | `event-v1` | | `topic` | Topic name (typically `all` for all events) | `all` | ### Example URL ```text wss://pulsar-ws-euw2.8x8.com/ws/v2/reader/persistent/my-tenant/event-v1/all ``` ## Regional Endpoints The 8x8 Event Streaming service is available in multiple AWS regions. Connect to the endpoint that corresponds to your 8x8 Contact Center deployment region. ### Available Regions | 8x8 Region | Hostname | Availability | |------------|--------------------------|--------------| | UK3 | `pulsar-ws-euw2.8x8.com` | Available | | US1 | `pulsar-ws-use1.8x8.com` | Available | | US2 | `pulsar-ws-usw2.8x8.com` | Available | > 📘 **Finding Your Region** > > Your region is determined by your Contact Center deployment location. If you're unsure which region to use, contact 8x8 Support or check your Admin Console settings. > > **Note:** Additional regions are being deployed progressively. This table will be updated as new regions become available. ### Using Regional Endpoints Use the hostname from the table above for your region: ```text wss://pulsar-ws-euw2.8x8.com/ws/v2/reader/persistent/{tenant}/{namespace}/{topic} ``` ## Available Topics The 8x8 Event Streaming service publishes events to multiple topics based on event type and agent. Subscribe to topics based on your requirements: ### All Events Topic Receive all events for your tenant: ```text persistent/{tenant}/event-v1/all ``` **Use case:** Monitoring all activity, building comprehensive dashboards, or logging all events. **Example:** ```text wss://pulsar-ws-euw2.8x8.com/ws/v2/reader/persistent/my-tenant/event-v1/all ``` ### Event Type Topics Receive only events of a specific type: ```text persistent/{tenant}/event-v1/{eventType} ``` **Available event types include:** - `InteractionCreated` - New interactions - `InteractionQueued` - Interactions waiting in queue - `InteractionAccepted` - Agent accepted an interaction - `InteractionDeleted` - Interaction completed or deleted - `AgentStatusChange` - Agent status changes - `AgentProvChange` - Agent provisioning changes - See [Event Reference](./event-reference.md) for complete list **Use case:** Applications that only need specific event types, reducing bandwidth and processing overhead. **Example:** ```text wss://pulsar-ws-euw2.8x8.com/ws/v2/reader/persistent/my-tenant/event-v1/InteractionCreated ``` ### Agent-Specific Topics Receive events for a specific agent: ```text persistent/{tenant}/agent-v1/{agentId} ``` **Use case:** Agent-specific dashboards, personal productivity tracking, or agent desktop integrations. **Example:** ```text wss://pulsar-ws-euw2.8x8.com/ws/v2/reader/persistent/my-tenant/agent-v1/agAglVJkg0TU28dok9y9UQKg ``` **Note:** Agent IDs are in the format shown in events (e.g., `agAglVJkg0TU28dok9y9UQKg`), not the full GUID. ### Topic Naming Convention Topics follow a versioned namespace pattern (`event-v1`, `agent-v1`) to allow for future compatibility and changes without breaking existing integrations. ## Reader vs Consumer API The 8x8 Event Streaming service supports both Pulsar's [**Reader API**](https://pulsar.apache.org/docs/next/client-libraries-readers/) and [**Consumer API**](https://pulsar.apache.org/docs/next/concepts-messaging/#consumers) via WebSocket. The examples in this documentation use the Reader API. ### Reader API The Reader API provides a lightweight interface for reading events: - **Simpler interface**: No subscription management required - **No acknowledgements**: Messages don't need to be acknowledged - **Position control**: You specify where to start reading (earliest, latest, or specific message) - **No cursor tracking**: Pulsar doesn't track your reading position - **Stateless**: Each connection is independent **Ideal for:** - Event streaming and monitoring - Building real-time dashboards - Simple message consumption without delivery guarantees - Development and debugging **WebSocket endpoint:** `/ws/v2/reader/persistent/{tenant}/{namespace}/{topic}` ### Consumer API The Consumer API provides more advanced features for production applications: - **Subscription management**: Durable subscriptions with cursor tracking - **Message acknowledgements**: Confirm message processing - **Multiple subscription types**: Exclusive, Shared, Failover, Key_Shared - **Load balancing**: Distribute messages across multiple consumers - **Dead letter queues**: Handle failed messages **Ideal for:** - Production applications requiring guaranteed delivery - Load-balanced message processing - Applications requiring message replay and acknowledgement **WebSocket endpoint:** `/ws/v2/consumer/persistent/{tenant}/{namespace}/{topic}/{subscription}` For more information, see: - [Apache Pulsar Reader documentation](https://pulsar.apache.org/docs/next/client-libraries-readers/) - [Apache Pulsar Consumer documentation](https://pulsar.apache.org/docs/next/concepts-messaging/#consumers) ## Query Parameters You can append query parameters to the WebSocket URL to configure the reader: | Parameter | Description | Values | Default | |---------------------|----------------------------------------|-------------------------------------|----------| | `x-api-key` | API key for authentication | Your API key | - | | `messageId` | Starting position for reading messages | `earliest`, `latest`, or message ID | `latest` | | `readerName` | Optional identifier for this reader | Any string | - | | `receiverQueueSize` | Size of the internal receiver queue | Integer > 0 | 1000 | ### Example with Query Parameters ```text wss://pulsar-ws-euw2.8x8.com/ws/v2/reader/persistent/my-company/event-v1/all?x-api-key=YOUR_KEY&messageId=earliest ``` ### URL-encoding (important) Query parameter values **must be URL-encoded**. Use your language's URL builder (`urllib.parse.urlencode` in Python, `URLSearchParams` in JavaScript, `URLEncoder.encode` in Java, `url.Values.Encode()` in Go) rather than building the query string with f-strings, template literals or string concatenation. This matters most for `messageId`. A specific Pulsar message ID is a base64-ish string that frequently contains `+`, `/` and `=` — all of which have special meaning in a URL query string. The tutorial-style `latest` and `earliest` values won't trigger the bug, so it tends to surface only once your code starts using a real message ID returned by a previous message. **Bad** — interpolating a raw `messageId` will silently break when it contains a `+`: ```python message_id = "CAEQAw+=" # value from a previous message url = f"wss://{host}/ws/v2/reader/persistent/{tenant}/event-v1/all?messageId={message_id}" # The server sees `messageId=CAEQAw =` (a literal space) and the read position is wrong. ``` **Good** — let the URL builder encode it: ```python from urllib.parse import urlencode message_id = "CAEQAw+=" query = urlencode({"messageId": message_id, "receiverQueueSize": 200}) url = f"wss://{host}/ws/v2/reader/persistent/{tenant}/event-v1/all?{query}" # messageId=CAEQAw%2B%3D — round-trips correctly. ``` ## Authentication Authentication is provided via the `X-API-Key` HTTP header or `x-api-key` query parameter. See the [Authentication Guide](./authentication.mdx) for complete details on credential types, obtaining keys, and implementation. ## Message Reading Position When you connect, you can specify where to start reading messages: ### Latest (Default) Start reading from the most recent message. You'll only receive new events that occur after your connection is established. ```text ?messageId=latest ``` ### Earliest Start reading from the beginning of the available message history. ```text ?messageId=earliest ``` ### Specific Message ID Start reading from a specific message (useful for resuming from a known position). ```text ?messageId=CAEQAQ== ``` ## Connection Timeout Configure an appropriate connection timeout (recommended: 45 seconds) to handle network latency and server processing time. ## Keepalive and Heartbeat WebSocket connections support ping/pong frames for keepalive: - The server may send **ping** frames to check connection health - Your client should respond with **pong** frames - Most WebSocket libraries handle this automatically ## Graceful Disconnection To close the connection cleanly: 1. Stop reading messages 2. Send a WebSocket **close** frame 3. Wait for the server's close acknowledgement 4. Close the underlying TCP connection 5. Most WebSocket libraries handle this automatically ## Next Steps - [Authentication Guide](./authentication.mdx) - Learn about authentication methods - [Message Format](./message-format.mdx) - Understand the message structure - [Code Examples](./examples/golang.md) - See complete working implementations --- ## Event Lifecycle Understanding how events flow through an interaction's lifecycle helps you build robust integrations and accurately track interaction progress. import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ## Overview Contact center interactions (calls, chats, emails) progress through a series of states, with each state transition generating an event. By tracking these events and their sequence, you can: - Build real-time dashboards showing interaction status - Calculate key metrics (queue time, talk time, wrap-up duration) - Correlate events to understand complete customer journeys - Identify patterns and optimize routing rules ## Inbound Call Lifecycle A typical inbound phone call generates events in this sequence: ```mermaid flowchart TD A[InteractionCreatedCS_IDLE] --> B[InteractionQueuedCS_QUEUED] B --> C[InteractionAssignedCS_INPROGRESSOffered to agent] C --> D[InteractionAcceptedCS_CONNECTEDAgent accepts] D --> E{Hold?} E -->|Optional| F[LineHoldStatusCS_HOLDAgent places on hold] F --> G[LineHoldStatusCS_CONNECTEDAgent takes off hold] G --> H[InteractionPostProcessCS_DISCONNECTEDCall ends, wrap-up begins] E -->|No hold| H H --> I[InteractionEndPostProcessCS_DISCONNECTEDWrap-up ends] I --> J[InteractionDeassignedCS_DISCONNECTEDAgent released] J --> K[InteractionDeletedCS_DISCONNECTEDFinal event] ``` **Key Fields to Track:** - `interactionGUID`: Unique identifier to correlate all events for this interaction - `callState`: Current state in the lifecycle - `interactionEventTS`: When each transition occurred. Not `eventTS`, which is the time the interaction was offered and is the same on every event (see [Choosing a Timestamp](./field-reference.mdx#choosing-a-timestamp)) - `agentId`: Which agent handled the interaction (from InteractionAssigned onward) ## Outbound Call Lifecycle Outbound calls (agent-initiated or campaign) have a slightly different flow: ```mermaid flowchart TD A[InteractionCreatedCS_IDLE] --> B[InteractionAssignedCS_INPROGRESSAssigned to agent] B --> C[Outbound dialing occurs] C --> D[InteractionCustomerAcceptedCS_CONNECTEDCustomer answers] D --> E{Hold?} E -->|Optional| F[LineHoldStatusCS_HOLD / CS_CONNECTED] F --> G[InteractionPostProcessCS_DISCONNECTED] E -->|No hold| G G --> H[InteractionEndPostProcessCS_DISCONNECTED] H --> I[InteractionDeassignedCS_DISCONNECTED] I --> J[InteractionDeletedCS_DISCONNECTED] ``` **Key Difference:** Outbound calls skip the `InteractionQueued` state and use `InteractionCustomerAccepted` when the customer answers. ## Rejected/Abandoned Call Flow Not all interactions complete successfully. Here are common alternate flows: ### Agent Rejects Interaction ```mermaid flowchart LR A[InteractionCreatedCS_IDLE] --> B[InteractionQueuedCS_QUEUED] B --> C[InteractionAssignedCS_INPROGRESS] C --> D[InteractionRejectedCS_QUEUEDAgent rejects] D --> E[Returns to queueMay be offered toanother agent] ``` When an agent rejects an offered interaction, it returns to the queue and may be offered to another agent. ### Customer Abandons Queue ```mermaid flowchart LR A[InteractionCreatedCS_IDLE] --> B[InteractionQueuedCS_QUEUED] B --> C[InteractionUnqueuedCS_IDLE or CS_DISCONNECTEDCustomer hangs up] C --> D[InteractionDeletedCS_DISCONNECTED] ``` If a customer hangs up while waiting in queue, you'll see `InteractionUnqueued` followed by `InteractionDeleted`. ### Outbound Call Not Answered ```mermaid flowchart LR A[InteractionCreatedCS_IDLE] --> B[InteractionAssignedCS_INPROGRESS] B --> C[No answer, busy, or failed] C --> D[InteractionDeletedCS_DISCONNECTEDhangupInitiator: SYSTEMcallHangupReason: CEC_DISCONNECT_NOANSWER] ``` When an outbound call doesn't connect, the `InteractionDeleted` event includes diagnostic information about why the call failed. ## Agent State Events In parallel with interaction events, agent state changes are tracked separately: ```mermaid flowchart TD A[Agent logs inAgentStatusChangenewState: 1 = LOGGED_IN] --> B[Agent goes availableAgentStatusChangenewState: 5 = AVAILABLE] B --> C{Skills/provisioningchanges?} C -->|Yes| D[AgentProvChange orAgentSkillChanged] D --> E[Agent handles interactions...] C -->|No| E E --> F[Agent goes unavailableAgentStatusChangenewState: different state] F --> G[Agent logs outAgentStatusChangenewState: 0 = LOGGED_OUT] ``` **Note:** Agent events are published to: - `tenant/event-v1/all` (all events topic) - `tenant/event-v1/AgentStatusChange` (event-specific topic) - `tenant/agent-v1/{agentId}` (agent-specific topic) ## Tracking Complete Interactions To track a complete interaction from start to finish: 1. **Use `interactionGUID`** as the correlation key across all events 2. **Track state transitions** via `callState` field 3. **Monitor timestamps** using `interactionEventTS` to calculate durations: - **Queue time**: InteractionQueued.interactionEventTS → InteractionAssigned.interactionEventTS - **Ring time**: InteractionAssigned.interactionEventTS → InteractionAccepted.interactionEventTS - **Talk time**: InteractionAccepted.interactionEventTS → InteractionPostProcess.interactionEventTS - **Wrap-up time**: InteractionPostProcess.interactionEventTS → InteractionEndPostProcess.interactionEventTS Use `interactionEventTS` for these, not `eventTS`. `eventTS` records when the interaction was offered, so it is identical on every event for that interaction and each of the subtractions above would evaluate to zero. See [Choosing a Timestamp](./field-reference.mdx#choosing-a-timestamp). 4. **Capture outcome** from InteractionDeleted: - `hangupInitiator`: Who ended the call - `callHangupReason`: Why the call ended - `dispositionCode`: Agent's wrap-up disposition ## Example: Building an Interaction Timeline Here's a practical example of tracking interaction metrics by correlating events: ```go type InteractionTimeline struct { InteractionGUID string Created time.Time Queued time.Time Assigned time.Time Connected time.Time Disconnected time.Time AgentID string QueueID int HangupReason string } func (t *InteractionTimeline) ProcessEvent(event Event) { // interactionEventTS is absent on some event types, where it unmarshals to 0. // Leave eventTime as the zero Time so the IsZero() guards below catch it - // time.Unix(0, 0) is 1970, which is NOT IsZero() and would slip through. var eventTime time.Time if event.InteractionEventTS > 0 { eventTime = time.Unix(event.InteractionEventTS, 0) } switch event.Event { case "InteractionCreated": t.InteractionGUID = event.InteractionGUID t.Created = eventTime case "InteractionQueued": t.Queued = eventTime t.QueueID = event.QueueID case "InteractionAssigned": t.Assigned = eventTime t.AgentID = event.AgentID case "InteractionAccepted", "InteractionCustomerAccepted": t.Connected = eventTime case "InteractionDeleted": t.Disconnected = eventTime t.HangupReason = event.CallHangupReason } } func (t *InteractionTimeline) QueueDuration() time.Duration { if t.Queued.IsZero() || t.Assigned.IsZero() { return 0 } return t.Assigned.Sub(t.Queued) } func (t *InteractionTimeline) TalkDuration() time.Duration { if t.Connected.IsZero() || t.Disconnected.IsZero() { return 0 } return t.Disconnected.Sub(t.Connected) } ``` ```python from datetime import datetime from dataclasses import dataclass from typing import Optional @dataclass class InteractionTimeline: interaction_guid: Optional[str] = None created: Optional[datetime] = None queued: Optional[datetime] = None assigned: Optional[datetime] = None connected: Optional[datetime] = None disconnected: Optional[datetime] = None agent_id: Optional[str] = None queue_id: Optional[int] = None hangup_reason: Optional[str] = None def process_event(self, event: dict): event_type = event.get('event') # interactionEventTS is absent on some event types - leave the time as None # rather than defaulting to 0, which would decode as 1970 event_ts = event.get('interactionEventTS') event_time = datetime.fromtimestamp(event_ts) if event_ts else None if event_type == 'InteractionCreated': self.interaction_guid = event['interactionGUID'] self.created = event_time elif event_type == 'InteractionQueued': self.queued = event_time self.queue_id = event.get('queueId') elif event_type == 'InteractionAssigned': self.assigned = event_time self.agent_id = event.get('agentId') elif event_type in ['InteractionAccepted', 'InteractionCustomerAccepted']: self.connected = event_time elif event_type == 'InteractionDeleted': self.disconnected = event_time self.hangup_reason = event.get('callHangupReason') def queue_duration(self) -> Optional[float]: if self.queued and self.assigned: return (self.assigned - self.queued).total_seconds() return None def talk_duration(self) -> Optional[float]: if self.connected and self.disconnected: return (self.disconnected - self.connected).total_seconds() return None ``` ```javascript class InteractionTimeline { constructor() { this.interactionGUID = null; this.created = null; this.queued = null; this.assigned = null; this.connected = null; this.disconnected = null; this.agentId = null; this.queueId = null; this.hangupReason = null; } processEvent(event) { // interactionEventTS is absent on some event types - leave the time as null // rather than defaulting to 0, which would decode as 1970 const eventTime = event.interactionEventTS ? new Date(event.interactionEventTS * 1000) : null; switch (event.event) { case 'InteractionCreated': this.interactionGUID = event.interactionGUID; this.created = eventTime; break; case 'InteractionQueued': this.queued = eventTime; this.queueId = event.queueId; break; case 'InteractionAssigned': this.assigned = eventTime; this.agentId = event.agentId; break; case 'InteractionAccepted': case 'InteractionCustomerAccepted': this.connected = eventTime; break; case 'InteractionDeleted': this.disconnected = eventTime; this.hangupReason = event.callHangupReason; break; } } queueDuration() { if (this.queued && this.assigned) { return (this.assigned - this.queued) / 1000; // seconds } return null; } talkDuration() { if (this.connected && this.disconnected) { return (this.disconnected - this.connected) / 1000; // seconds } return null; } } ``` ## Best Practices ### Event Correlation - **Always use `interactionGUID`** to correlate events - it's the only reliable way to track an interaction across its entire lifecycle - **Store events** rather than just metrics - you may need to recalculate or debug later - **Handle out-of-order events** - network delays can cause events to arrive out of sequence ### State Management - **Track `callState` transitions** to ensure your application state matches the interaction state - **Expect optional events** - not all interactions follow the complete path (e.g., abandoned calls skip most steps) - **Handle missing events** - network issues or system restarts may cause missed events ### Metric Calculation - **Use `interactionEventTS`** for duration calculations - not `eventTS`, which does not advance between events (see [Choosing a Timestamp](./field-reference.mdx#choosing-a-timestamp)) - **Handle events with no `interactionEventTS`** - use `msgInfo.timestamp` rather than defaulting a missing value to 0 - **Account for holds** - `LineHoldStatus` events affect talk time calculations - **Consider time zones** - timestamps are in UTC, convert as needed for reporting ### Error Handling - **Check `hangupInitiator`** and `callHangupReason` in InteractionDeleted events to understand why interactions ended - **Monitor for anomalies** - unexpected state transitions may indicate system issues - **Log unhandled event types** - new event types may be added in future releases ## Next Steps - [Message Format](./message-format.mdx) - Detailed field descriptions and message structure - [Event Reference](./event-reference.md) - Complete list of event types and their fields - [Field Reference](./field-reference.mdx) - Detailed documentation for all event fields - [Code Examples](./examples/golang.md) - Complete working implementations --- ## Event Reference This page provides a complete catalog of all event types available in the 8x8 Event Streaming service, organized by category, along with detailed JSON examples for common events. ## Event Types by Category The 8x8 Event Streaming service provides the following event types: ### Agent Events | Event Type | Call State | Description | |---------------------|------------|---------------------------------------------------------------| | `AgentStatusChange` | N/A | Indicates the current status of the monitored agent | | `AgentProvChange` | N/A | Indicates a change to the agent profile (skills, etc.) | | `AgentSkillCreated` | N/A | Indicates a new agent skill has been created | | `AgentSkillChanged` | N/A | Indicates an agent skill has been modified (enabled/disabled) | ### Interaction Events | Event Type | Call State | Description | |---------------------------------------|---------------------------------|-------------------------------------------------------------------| | `InteractionCreated` | CS_IDLECS_QUEUED | Indicates a new outbound or inbound interaction creation | | `InteractionQueued` | CS_QUEUED | Indicates an interaction waiting to be assigned to an agent | | `InteractionAssigned` | CS_INPROGRESS | Indicates an interaction assigned to an available agent | | `InteractionAccepted` | CS_INPROGRESSCS_CONNECTED | Indicates an interaction accepted by an agent | | `InteractionCustomerAccepted` | CS_CONNECTED | Indicates a ringing outbound call answered by the customer | | `InteractionRejected` | CS_QUEUED | Indicates an agent rejected an offered interaction | | `InteractionUnqueued` | CS_IDLECS_QUEUED | Indicates an interaction removed from queue before assignment | | `LineHoldStatus` | CS_HOLDCS_CONNECTED | Indicates hold status change for an ongoing call | | `LineMuteStatus` | CS_CONNECTED | Indicates mute status change for an ongoing call | | `InteractionRecordingStarted` | N/A | Indicates the system has started recording a call leg | | `RecordingStatus` | N/A | Indicates a recording status change (pause/resume) | | `InteractionPostProcess` | CS_DISCONNECTED | Indicates the start of interaction wrap-up | | `InteractionRecordingReady` | N/A | Indicates an interaction recording is ready for retrieval | | `InteractionEndPostProcess` | CS_DISCONNECTED | Indicates the conclusion of interaction wrap-up | | `InteractionDeassigned` | CS_DISCONNECTED | Indicates an interaction deassigned from an agent | | `InteractionTransferRequest` | N/A | Indicates a transfer request for an interaction | | `InteractionReconnect` | N/A | Indicates an interaction reconnect event | | `InteractionParticipantChange` | CS_DISCONNECTED | Indicates participant changes (conference, supervisor join, etc.) | | `InteractionParticipantRemovedByHost` | N/A | Indicates a participant removed from interaction by host | | `InteractionJoinLinesRequest` | N/A | Indicates a request to join multiple lines | | `InteractionJoined` | N/A | Indicates multiple lines have been joined | | `InteractionChanged` | N/A | Indicates interaction properties have changed | | `InteractionQueueTimeout` | N/A | Indicates an interaction timed out in queue | | `InteractionDeleted` | CS_DISCONNECTED | Indicates interaction ended or call leg left conference | ### Digital Channel Events | Event Type | Description | |----------------|------------------------------------------| | `GuestChatEnd` | Indicates a guest chat session has ended | `GuestChatEnd` carries no `interactionEventTS`. Use `msgInfo.timestamp` for its time - see [Choosing a Timestamp](./field-reference.mdx#choosing-a-timestamp). ### Media/Proxy Events | Event Type | Description | |---------------------|------------------------------------------| | `MediaProxyAdded` | Indicates a media proxy has been added | | `MediaProxyRemoved` | Indicates a media proxy has been removed | ## Event Payload Examples This section provides complete JSON examples for common event types. For field definitions, see the [Field Reference](./field-reference.mdx). ### AgentStatusChange ```json { "AgentStatusChange": { "agentId": "ag64oyEUb_Sk6bxVB9P5yaaa", "msgInfo": { "instanceId": "example.8x8.com", "sequenceId": 215, "timestamp": 1669235544917 }, "newReasonCodeUser": "801=1722", "newState": 5, "newSubState": "none", "newSubStateReason": "none", "statusEventTS": 1669235544 } } ``` **Field Descriptions:** - `agentId`: Unique agent identifier - `newState`: Numeric state code - `newSubState`: Sub-state description - `newReasonCodeUser`: User-defined reason code - `statusEventTS`: Status event timestamp (seconds since epoch) ### AgentProvChange / AgentSkillChanged This event indicates a change to agent provisioning, such as skills being added, removed, or modified. ```json { "AgentProvChange": { "agentId": "agUeZBQ3RnQTaL0qZZukNOaw", "enabled": "yes", "event": "AgentSkillChanged", "msgInfo": {}, "skillId": "tenant01-agUeZBQ3RnQTaL0qZZukNOaw-phone-529-f01e6bdb-75a2-4639-825c-91c0f5168258", "tenantSkillId": "tenant01-phone-529-b1e3e2f2-b01a-4a03-ba3d-f30859414d97" } } ``` **Field Descriptions:** - `event`: Type of provisioning change ("AgentSkillChanged", "AgentSkillCreated", etc.) - `enabled`: Whether the skill/feature is enabled ("yes"/"no") - `skillId`: Full skill identifier including agent ID and GUID - `tenantSkillId`: Tenant-specific skill ID with GUID **Note:** The `skillId` and `tenantSkillId` formats include GUIDs that uniquely identify the skill assignment. ### InteractionCreated ```json { "Interaction": { "attachedData": { "attachedDatum": [ {"attachedDataKey": "@pri", "attachedDataValue": 100}, {"attachedDataKey": "callingName", "attachedDataValue": "Andrew Cunningh"}, {"attachedDataKey": "phoneNum", "attachedDataValue": 5515557212} ] }, "callState": "CS_QUEUED", "event": "InteractionCreated", "interactionGUID": "int-184a63564dc-ohWfVIbHJz2Hr2JFhAfdlb4Fa-phone-00-acmecorp01", "msgInfo": { "instanceId": "example.8x8.com", "sequenceId": 220, "timestamp": 1669235700000 }, "resourceType": 0 } } ``` ### InteractionQueued ```json { "Interaction": { "attachedData": { "attachedDatum": [ {"attachedDataKey": "@pri", "attachedDataValue": 100}, {"attachedDataKey": "callingName", "attachedDataValue": 447799147855}, {"attachedDataKey": "cha", "attachedDataValue": 441733968848}, {"attachedDataKey": "channelName", "attachedDataValue": "Support Queue"}, {"attachedDataKey": "phoneNum", "attachedDataValue": 447799147855}, {"attachedDataKey": "priority", "attachedDataValue": 50}, {"attachedDataKey": "queueDirection", "attachedDataValue": "in"}, {"attachedDataKey": "tenantName", "attachedDataValue": "tenant01"}, {"attachedDataKey": "tenantSkillName", "attachedDataValue": "Customer Service"} ] }, "callState": "CS_QUEUED", "callerPermission": "yes", "direction": "in", "event": "InteractionQueued", "eventTS": 1744819217, "inboundChannelid": 441733968848, "interactionEventTS": 1744819217, "interactionGUID": "int-1963f541b9f-wgyWcn0J0hwtyhN4XfMHIfCIO-phone-01-tenant01", "isAgentInitiated": false, "mediaType": "phone", "msgInfo": {}, "overrideAutoInteractionRecording": "yes", "priority": 50, "queueId": 1162, "queueList": 1162, "queueTime": 1744819217, "resourceType": 0, "transactionNum": 848184 } } ``` **Field Descriptions:** - `direction`: "in" for inbound, "out" for outbound - `mediaType`: Communication channel ("phone", "chat", "email") - `priority`: Queue priority (higher numbers = higher priority) - `queueId`: Numeric queue identifier - `queueList`: Queue list identifier - `queueTime`: Time when interaction entered queue (seconds since epoch) - `eventTS`: When the interaction was offered (seconds since epoch). The same on every event for this interaction - use `interactionEventTS` for the time this event occurred (see [Choosing a Timestamp](./field-reference.mdx#choosing-a-timestamp)) - `transactionNum`: Transaction number - `callerPermission`: Whether caller granted recording permission - `overrideAutoInteractionRecording`: Recording override setting ### InteractionDeleted ```json { "Interaction": { "attachedData": { "attachedDatum": [ {"attachedDataKey": "callingName", "attachedDataValue": "Andrew Cunningh"}, {"attachedDataKey": "phoneNum", "attachedDataValue": 5515557212} ] }, "callHangupReason": "CEC_DISCONNECT_NORMAL", "callState": "CS_DISCONNECTED", "dispositionCode": 1000, "event": "InteractionDeleted", "hangupInitiator": "CUSTOMER", "interactionGUID": "int-184a63564dc-ohWfVIbHJz2Hr2JFhAfdlb4Fa-phone-00-acmecorp01", "msgInfo": { "instanceId": "example.8x8.com", "sequenceId": 235, "timestamp": 1669236000000 } } } ``` **Field Descriptions:** - `hangupInitiator`: Who ended the call ("AGENT", "CUSTOMER", "SYSTEM") - `callHangupReason`: Reason code for hangup (see Hangup Reasons below) - `dispositionCode`: Call disposition code **Hangup Reasons:** - `CEC_NONE`: No specific reason - `CEC_DISCONNECT_NORMAL`: Normal call termination - `CEC_DISCONNECT_BUSY`: Number was busy - `CEC_DISCONNECT_BADADDRESS`: Invalid phone number - `CEC_DISCONNECT_NOANSWER`: No answer - `CEC_DISCONNECT_CANCELLED`: Call cancelled - `CEC_DISCONNECT_REJECTED`: Call rejected - `CEC_DISCONNECT_FAILED`: Call failed - `CEC_DISCONNECT_BLOCKED`: Call blocked ### InteractionAssigned Indicates an interaction has been assigned to an available agent. This event occurs before the agent accepts the interaction. ```json { "Interaction": { "agentGUID": "tenant01-agAglVJkg0TU28dok9y9UQKg-e46672cd-660c-46f5-b8f0-856eaf18f65c", "agentId": "agAglVJkg0TU28dok9y9UQKg", "agentPhone": 40375, "agentRscWaitTime": 30, "agentTreatedPhone": "40375-tenant", "attachedData": { "attachedDatum": [ {"attachedDataKey": "@pri", "attachedDataValue": 50}, {"attachedDataKey": "phoneNum", "attachedDataValue": 6298994259}, {"attachedDataKey": "tenantSkillName", "attachedDataValue": "Sales Queue"} ] }, "callState": "CS_INPROGRESS", "direction": "out", "event": "InteractionAssigned", "eventTS": 1744819092, "interactionGUID": "int-1963f527a2a-9wjz0hrkcolMxIqiOhxEsKFe9-phone-01-tenant01", "isAgentInitiated": false, "isDirectAccess": false, "isExternal": true, "isOutboundCall": true, "mediaType": "phone", "msgInfo": {}, "outboundCampaignid": 4926, "participatingAgents": "agAglVJkg0TU28dok9y9UQKg", "promptingTimeout": 30, "queueId": 1092, "queueList": 1092, "queueTime": 1744819092, "recordingMode": "no", "resourceType": 0, "transactionNum": 279563 } } ``` **Field Descriptions:** - `agentPhone`: Agent's phone number - `agentRscWaitTime`: Time agent will wait for assignment (seconds) - `agentTreatedPhone`: Formatted agent phone number - `promptingTimeout`: Timeout for agent to respond (seconds) - `outboundCampaignid`: Campaign ID (0 if not a campaign call) - `isOutboundCall`: Boolean indicating outbound call ### InteractionRejected Indicates an agent rejected an offered interaction (did not accept it). ```json { "Interaction": { "agentGUID": "tenant01-ag9I1yUNAvRqG7ovVMi3NTyw-c40cf425-5b69-4575-99a7-ccd6457b0180", "agentId": "ag9I1yUNAvRqG7ovVMi3NTyw", "callState": "CS_QUEUED", "event": "InteractionRejected", "interactionEventTS": 1744819218, "interactionGUID": "int-1963f53a7f9-fRUlBL19w0hsi4GN50Xbcgazp-phone-03-tenant01", "msgInfo": {}, "participatingAgents": "ag9I1yUNAvRqG7ovVMi3NTyw", "queueList": 222, "rejectReason": 2, "resourceType": 0 } } ``` **Field Descriptions:** - `rejectReason`: Numeric reason code for rejection (see [Field Reference](./field-reference.mdx#reject-reason-codes)) ### LineHoldStatus Indicates hold status change for an ongoing call. The `status` field indicates whether the call is being placed on hold (true) or taken off hold (false). ```json { "Interaction": { "agentGUID": "tenant01-agGiRMDKqSRGuaNOelIJosUw-494708fb-81d3-415a-82da-c5d800d36f18", "agentId": "agGiRMDKqSRGuaNOelIJosUw", "callState": "CS_HOLD", "event": "LineHoldStatus", "interactionEventTS": 1744819224, "interactionGUID": "int-1963f52d470-OcB3AbajqjTb9ikyLLBxBr1l7-phone-02-tenant01", "mediaType": "phone", "msgInfo": {}, "participatingAgents": "agGiRMDKqSRGuaNOelIJosUw", "status": true, "transactionNum": 429968 } } ``` **Field Descriptions:** - `status`: `true` = call placed on hold (CS_HOLD), `false` = call taken off hold (CS_CONNECTED) ### RecordingStatus Indicates a recording status change (pause or resume) for an ongoing interaction. ```json { "Interaction": { "agentGUID": "tenant01-agFDUfx__VREaRSfGYYm5cNw-ad92a3d3-414a-4dfa-a971-dc0d28970d22", "agentId": "agFDUfx__VREaRSfGYYm5cNw", "event": "RecordingStatus", "interactionEventTS": 1744819223, "interactionGUID": "int-1963f4d8314-ShlIYL3nayugzbFYJvHhZgPL2-phone-03-tenant01", "isAPICall": false, "msgInfo": {}, "recordingMode": "yes", "status": "resume", "transactionNum": 5511 } } ``` **Field Descriptions:** - `status`: Recording status - "resume" or "pause" - `isAPICall`: Whether the status change was triggered via API - `recordingMode`: Overall recording mode for interaction ## Next Steps - [Message Format](./message-format.mdx) - Learn how to decode and process messages - [Field Reference](./field-reference.mdx) - Detailed field documentation - [Code Examples](./examples/golang.md) - See event processing in context --- ## Browser Client Example Web-based tool for testing, debugging, and monitoring 8x8 Event Streams in real-time. ## Accessing the Pulsar UI The Pulsar UI is available at: [https://cloud8.8x8.com/vcc-cloud8-pulsar-ui/ui](https://cloud8.8x8.com/vcc-cloud8-pulsar-ui/ui) ![Pulsar UI - Connection Form](../../../images/pulsar-ui-connection.png) Fill in your region, tenant name, and API key (see [Authentication Guide](../authentication.mdx)), then click **Subscribe**. ![Pulsar UI - Events Streaming](../../../images/pulsar-ui-events.png) ## Next Steps - [Go Client Example](./golang.md) - For command-line usage and automation - [Java Client Example](./java.md) - For Java-based applications - [Python Client Example](./python.mdx) - For Python-based applications - [Node.js Client Example](./nodejs.mdx) - For JavaScript/Node.js applications - [Connection Guide](../connection.md) - Learn more about WebSocket connections - [Message Format](../message-format.mdx) - Understanding event structure --- ## Go Client Example This page provides a complete Go implementation for connecting to the 8x8 Event Streaming service. ## Overview The Go client example demonstrates: - WebSocket connection setup - Authentication using X-API-Key - Message reading and payload decoding - Error handling and graceful shutdown ## Complete Example ```go package main import ( "encoding/base64" "encoding/json" "flag" "fmt" "log" "net/http" "net/url" "time" "github.com/gorilla/websocket" ) // PulsarMessage represents a message received from Pulsar WebSocket type PulsarMessage struct { // Data message fields MessageID string `json:"messageId"` Payload string `json:"payload"` // Base64 encoded Properties map[string]string `json:"properties,omitempty"` PublishTime string `json:"publishTime,omitempty"` RedeliveryCount int `json:"redeliveryCount,omitempty"` // Control message fields Type string `json:"type,omitempty"` // e.g., "isEndOfTopic" EndOfTopic string `json:"endOfTopic,omitempty"` // "true" or "false" } // IsControlMessage returns true if this is a control message (should not be acknowledged) func (m *PulsarMessage) IsControlMessage() bool { return m.Type != "" || m.EndOfTopic != "" } // DecodePayload decodes the base64 payload func (m *PulsarMessage) DecodePayload() ([]byte, error) { return base64.StdEncoding.DecodeString(m.Payload) } // extractPulsarPayload parses a Pulsar message and extracts the decoded payload func extractPulsarPayload(data []byte) (*PulsarMessage, []byte, error) { var msg PulsarMessage if err := json.Unmarshal(data, &msg); err != nil { return nil, nil, fmt.Errorf("failed to parse Pulsar message: %w", err) } payload, err := msg.DecodePayload() if err != nil { return &msg, nil, fmt.Errorf("failed to decode payload: %w", err) } return &msg, payload, nil } // sendAck sends an acknowledgment message for a received message // This is REQUIRED for WebSocket readers to prevent backlog buildup and message delivery stoppage func sendAck(conn *websocket.Conn, messageID string) error { ackMsg := map[string]string{"messageId": messageID} ackJSON, err := json.Marshal(ackMsg) if err != nil { return fmt.Errorf("failed to marshal ack message: %w", err) } err = conn.WriteMessage(websocket.TextMessage, ackJSON) if err != nil { return fmt.Errorf("failed to send ack: %w", err) } return nil } // buildURL constructs the Pulsar WebSocket URL func buildURL(host string, port int, tenant, namespace, topic, xAPIKey string) (string, error) { baseURL := fmt.Sprintf("wss://%s:%d/ws/v2/reader/persistent/%s/%s/%s", host, port, tenant, namespace, topic) u, err := url.Parse(baseURL) if err != nil { return "", fmt.Errorf("invalid URL: %w", err) } // URL query parameter values MUST be URL-encoded. url.Values.Encode() handles // this for you -- do NOT build query strings with fmt.Sprintf/concatenation. // Pulsar message IDs in particular contain '+', '/' and '=' characters that // have special meaning in a URL and will silently break a naive query string. if xAPIKey != "" { q := u.Query() q.Set("x-api-key", xAPIKey) u.RawQuery = q.Encode() } return u.String(), nil } // ConnectAndReceive connects to a WebSocket URL and receives messages func ConnectAndReceive(wsURL string, xAPIKey string) error { // Set up HTTP headers for authentication headers := http.Header{} // Add X-API-Key header if provided if xAPIKey != "" { headers.Set("X-API-Key", xAPIKey) } // Configure WebSocket dialer dialer := websocket.Dialer{ HandshakeTimeout: 45 * time.Second, } // Connect to WebSocket log.Printf("Connecting to WebSocket...") conn, resp, err := dialer.Dial(wsURL, headers) if err != nil { if resp != nil { return fmt.Errorf("failed to connect to WebSocket (status: %d): %w", resp.StatusCode, err) } return fmt.Errorf("failed to connect to WebSocket: %w", err) } defer conn.Close() log.Printf("Successfully connected") // Read messages continuously for { messageType, message, err := conn.ReadMessage() if err != nil { log.Printf("Error reading message: %v", err) return err } switch messageType { case websocket.TextMessage, websocket.BinaryMessage: // Extract and decode Pulsar message payload pulsarMsg, payload, err := extractPulsarPayload(message) if err != nil { log.Printf("Error extracting payload: %v", err) continue } // Check if this is a control message (don't print or ack these) if pulsarMsg.IsControlMessage() { continue } // Print only the payload fmt.Println(string(payload)) // Send acknowledgment (required for WebSocket flow control) if err := sendAck(conn, pulsarMsg.MessageID); err != nil { log.Printf("Warning: Failed to send ack: %v", err) } case websocket.CloseMessage: log.Println("Received close message from server") return nil } } } func main() { // Connection parameters // Default host: pulsar-ws-euw2.8x8.com (example uses euw2 region; for other regions see developer.8x8.com) host := flag.String("host", "pulsar-ws-euw2.8x8.com", "Pulsar broker hostname") port := flag.Int("port", 443, "Pulsar broker port") tenant := flag.String("tenant", "", "Pulsar tenant name (required)") namespace := flag.String("namespace", "event-v1", "Pulsar namespace") topic := flag.String("topic", "all", "Pulsar topic name") xAPIKey := flag.String("x-api-key", "", "X-API-Key header value") flag.Parse() // Validate required parameters if *tenant == "" { log.Fatal("Error: -tenant is required") } // Build full URL fullURL, err := buildURL(*host, *port, *tenant, *namespace, *topic, *xAPIKey) if err != nil { log.Fatalf("Error building URL: %v", err) } // Connect and receive messages if err := ConnectAndReceive(fullURL, *xAPIKey); err != nil { log.Fatalf("Error: %v", err) } } ``` ## Key Components ### Message Structure The `PulsarMessage` struct maps to the JSON structure received from Pulsar: ```go type PulsarMessage struct { MessageID string `json:"messageId"` Payload string `json:"payload"` // Base64 encoded Properties map[string]string `json:"properties,omitempty"` PublishTime string `json:"publishTime,omitempty"` RedeliveryCount int `json:"redeliveryCount,omitempty"` } ``` ### URL Construction The client builds the WebSocket URL from components: ```go baseURL := fmt.Sprintf("wss://%s:%d/ws/v2/reader/persistent/%s/%s/%s", host, port, tenant, namespace, topic) ``` ### Authentication X-API-Key is set both as a header and query parameter: ```go headers := http.Header{} headers.Set("X-API-Key", xAPIKey) // Also add to URL query params q := u.Query() q.Set("x-api-key", xAPIKey) ``` ### Payload Decoding The payload is base64 encoded and must be decoded before use: ```go payload, err := base64.StdEncoding.DecodeString(pulsarMsg.Payload) ``` ### Message Acknowledgement Acknowledgements are required to prevent message delivery stoppage: ```go ackMsg := map[string]string{"messageId": messageID} ackJSON, _ := json.Marshal(ackMsg) conn.WriteMessage(websocket.TextMessage, ackJSON) ``` ### Control Message Filtering Control messages (like end-of-topic markers) should not be processed: ```go if pulsarMsg.IsControlMessage() { continue } ``` ## Running the Example ### Prerequisites ```bash go install github.com/gorilla/websocket@latest ``` ### Build and Run ```bash # Build go build -o pulsar-client main.go # Run ./pulsar-client \ -tenant YOUR_TENANT \ -x-api-key YOUR_API_KEY ``` ### Using Environment Variables ```bash export PULSAR_TENANT=your-tenant export PULSAR_API_KEY=your-api-key ./pulsar-client \ -tenant $PULSAR_TENANT \ -x-api-key $PULSAR_API_KEY ``` ## Processing with jq Pipe the output to `jq` for JSON processing: ```bash # Pretty-print all events ./pulsar-client -tenant YOUR_TENANT -x-api-key YOUR_KEY | jq . # Filter by event type ./pulsar-client -tenant YOUR_TENANT -x-api-key YOUR_KEY | \ jq 'select(.eventType == "agent.login")' # Extract specific fields ./pulsar-client -tenant YOUR_TENANT -x-api-key YOUR_KEY | \ jq '{type: .eventType, time: .timestamp}' ``` ## Error Handling The example includes error handling for: - Invalid URL construction - Connection failures with HTTP status codes - WebSocket read errors - JSON parsing errors - Base64 decoding errors - Acknowledgement send failures ## Next Steps - [Java Client Example](./java.md) - See the same functionality in Java - [Message Format](../message-format.mdx) - Learn more about message structure - [Troubleshooting](../troubleshooting.md) - Common issues and solutions --- ## Java Client Example This page provides a complete Java implementation for connecting to the 8x8 Event Streaming service. ## Overview The Java client example demonstrates: - WebSocket connection using Java-WebSocket library - Authentication using X-API-Key - Message reading and payload decoding - Command-line argument parsing - Error handling with proper logging ## Complete Example ```java package com._8x8.pulsar; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.Data; import org.java_websocket.client.WebSocketClient; import org.java_websocket.handshake.ServerHandshake; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.URI; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.util.Base64; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; /** * Simplified WebSocket client for consuming 8x8 streaming events via Apache Pulsar. * * This client connects to Pulsar's WebSocket Reader endpoint and outputs raw message payloads * suitable for piping to tools like jq. */ public class SimpleClient { private static final Logger log = LoggerFactory.getLogger(SimpleClient.class); private static final ObjectMapper objectMapper = new ObjectMapper(); /** * Pulsar message structure from WebSocket. */ @Data static class PulsarMessage { // Data message fields @JsonProperty("messageId") private String messageId; @JsonProperty("payload") private String payload; // Base64 encoded @JsonProperty("properties") private Map properties = new HashMap<>(); @JsonProperty("publishTime") private String publishTime; @JsonProperty("redeliveryCount") private int redeliveryCount; // Control message fields @JsonProperty("type") private String type; // e.g., "isEndOfTopic" @JsonProperty("endOfTopic") private String endOfTopic; // "true" or "false" /** * Check if this is a control message (should not be acknowledged). */ public boolean isControlMessage() { return (type != null && !type.isEmpty()) || (endOfTopic != null && !endOfTopic.isEmpty()); } /** * Decode the base64 payload. */ public byte[] decodePayload() { return Base64.getDecoder().decode(payload); } /** * Decode payload as UTF-8 string. */ public String decodePayloadAsString() { return new String(decodePayload(), StandardCharsets.UTF_8); } } /** * Build Pulsar WebSocket URL. */ private static String buildUrl(String host, int port, String tenant, String namespace, String topic, String xApiKey) { try { String baseUrl = String.format("wss://%s:%d/ws/v2/reader/persistent/%s/%s/%s", host, port, tenant, namespace, topic); // URL query parameter values MUST be URL-encoded -- always run them // through URLEncoder.encode(). Do NOT concatenate raw values into the // query string. Pulsar message IDs in particular contain '+', '/' and // '=' characters that have special meaning in a URL and will silently // break a naive query string. if (xApiKey != null && !xApiKey.isEmpty()) { baseUrl += "?x-api-key=" + URLEncoder.encode(xApiKey, StandardCharsets.UTF_8); } return baseUrl; } catch (Exception e) { throw new RuntimeException("Failed to build URL", e); } } /** * WebSocket client implementation. */ static class PulsarWebSocketClient extends WebSocketClient { private final CountDownLatch latch = new CountDownLatch(1); private final String xApiKey; public PulsarWebSocketClient(URI serverUri, String xApiKey) { super(serverUri); this.xApiKey = xApiKey; // Add X-API-Key header if provided if (xApiKey != null && !xApiKey.isEmpty()) { addHeader("X-API-Key", xApiKey); } // Set connection timeout setConnectionLostTimeout(45); } @Override public void onOpen(ServerHandshake handshake) { log.info("Successfully connected"); } @Override public void onMessage(String message) { try { // Parse Pulsar message PulsarMessage pulsarMsg = objectMapper.readValue(message, PulsarMessage.class); // Check if this is a control message (don't print or ack these) if (pulsarMsg.isControlMessage()) { return; } // Decode and print only the payload String payload = pulsarMsg.decodePayloadAsString(); System.out.println(payload); // Send acknowledgment (required for WebSocket flow control) sendAck(pulsarMsg.getMessageId()); } catch (Exception e) { log.error("Error processing message: {}", e.getMessage()); } } /** * Send an acknowledgment message for a received message. * This is REQUIRED for WebSocket readers to prevent backlog buildup and message delivery stoppage. */ private void sendAck(String messageId) { try { Map ackMsg = new HashMap<>(); ackMsg.put("messageId", messageId); String ackJson = objectMapper.writeValueAsString(ackMsg); send(ackJson); } catch (Exception e) { log.warn("Failed to send ack for messageId: {}", messageId, e); } } @Override public void onClose(int code, String reason, boolean remote) { log.info("Connection closed: {} - {}", code, reason); latch.countDown(); } @Override public void onError(Exception ex) { log.error("WebSocket error: {}", ex.getMessage()); latch.countDown(); } public void awaitClose() throws InterruptedException { latch.await(); } } /** * Parse command-line arguments. */ private static Map parseArgs(String[] args) { Map params = new HashMap<>(); // Defaults // Example uses euw2 region. For other regions, see developer.8x8.com params.put("host", "pulsar-ws-euw2.8x8.com"); params.put("port", "443"); params.put("namespace", "event-v1"); params.put("topic", "all"); // Get API key from environment variable String apiKey = System.getenv("PULSAR_API_KEY"); if (apiKey != null && !apiKey.isEmpty()) { params.put("x-api-key", apiKey); } for (int i = 0; i < args.length; i++) { String arg = args[i]; if (arg.startsWith("--") && i + 1 < args.length) { String key = arg.substring(2); String value = args[++i]; params.put(key, value); } } return params; } /** * Main entry point. */ public static void main(String[] args) { PulsarWebSocketClient client = null; try { // Parse arguments Map params = parseArgs(args); // Validate required parameters if (!params.containsKey("tenant")) { System.err.println("Error: --tenant is required"); System.exit(1); } // Build URL String url = buildUrl( params.get("host"), Integer.parseInt(params.get("port")), params.get("tenant"), params.get("namespace"), params.get("topic"), params.get("x-api-key") ); log.info("Connecting to WebSocket..."); // Create and connect WebSocket client client = new PulsarWebSocketClient( new URI(url), params.get("x-api-key") ); // Connect (blocking) if (!client.connectBlocking()) { log.error("Failed to connect to WebSocket"); System.exit(1); } // Wait for connection to close client.awaitClose(); } catch (Exception e) { log.error("Error: {}", e.getMessage(), e); System.exit(1); } finally { if (client != null) { try { client.close(); } catch (Exception e) { log.warn("Error closing WebSocket client: {}", e.getMessage()); } } } } } ``` ## Key Components ### Message Structure The `PulsarMessage` class uses Jackson annotations for JSON mapping: ```java @Data static class PulsarMessage { // Data message fields @JsonProperty("messageId") private String messageId; @JsonProperty("payload") private String payload; // Base64 encoded @JsonProperty("properties") private Map properties; @JsonProperty("publishTime") private String publishTime; @JsonProperty("redeliveryCount") private int redeliveryCount; // Control message fields @JsonProperty("type") private String type; @JsonProperty("endOfTopic") private String endOfTopic; } ``` ### URL Construction The URL is built with proper encoding: ```java String baseUrl = String.format("wss://%s:%d/ws/v2/reader/persistent/%s/%s/%s", host, port, tenant, namespace, topic); if (xApiKey != null && !xApiKey.isEmpty()) { baseUrl += "?x-api-key=" + URLEncoder.encode(xApiKey, StandardCharsets.UTF_8); } ``` ### Authentication X-API-Key is added as a header during connection: ```java if (xApiKey != null && !xApiKey.isEmpty()) { addHeader("X-API-Key", xApiKey); } ``` ### Payload Decoding The payload is decoded from base64: ```java public byte[] decodePayload() { return Base64.getDecoder().decode(payload); } public String decodePayloadAsString() { return new String(decodePayload(), StandardCharsets.UTF_8); } ``` ### Message Acknowledgement Acknowledgements are required to prevent message delivery stoppage: ```java Map ackMsg = new HashMap<>(); ackMsg.put("messageId", messageId); String ackJson = objectMapper.writeValueAsString(ackMsg); send(ackJson); ``` ### Control Message Filtering Control messages (like end-of-topic markers) should not be processed: ```java if (pulsarMsg.isControlMessage()) { return; } ``` ## Maven Dependencies Add these dependencies to your `pom.xml`: ```xml org.java-websocket Java-WebSocket 1.5.7 com.fasterxml.jackson.core jackson-databind 2.18.1 org.projectlombok lombok 1.18.36 provided ch.qos.logback logback-classic 1.5.12 ``` ## Building and Running ### Build with Maven ```bash mvn clean package ``` ### Run the JAR ```bash java -jar target/pulsar-simple-client.jar \ --tenant YOUR_TENANT \ --x-api-key YOUR_API_KEY ``` ### Using Environment Variables ```bash export PULSAR_API_KEY=your-api-key java -jar target/pulsar-simple-client.jar \ --tenant YOUR_TENANT ``` ## Configuration Options | Option | Description | Default | |---------------|---------------------------------|-------------------------------| | `--tenant` | Your 8x8 tenant name (required) | - | | `--host` | Pulsar broker hostname | pulsar-ws-euw2.8x8.com (EUW2) | | `--port` | Pulsar broker port | 443 | | `--namespace` | Pulsar namespace | event-v1 | | `--topic` | Topic name | all | | `--x-api-key` | API key for authentication | (from PULSAR_API_KEY env var) | ## Error Handling The example includes error handling for: - Connection failures - Message parsing errors - Base64 decoding errors - WebSocket errors and disconnections - Acknowledgement send failures (logged as warnings) Errors are logged using SLF4J with Logback. ## Next Steps - [Go Client Example](./golang.md) - See the same functionality in Go - [Message Format](../message-format.mdx) - Learn more about message structure - [Troubleshooting](../troubleshooting.md) - Common issues and solutions --- ## Node.js Client Example This page provides a complete Node.js implementation for connecting to the 8x8 Event Streaming service. ## Overview The Node.js client example demonstrates: - WebSocket connection using the `ws` library - Authentication using X-API-Key - Message reading and payload decoding - Modern async/await syntax - Command-line argument parsing - Clean, pipe-friendly output for integration with tools like `jq` ## Prerequisites - **Node.js 14 or higher** (includes required async/await and modern JavaScript support) - **npm** package manager - **ws library** (version 8.16.0 or higher) ## Installation ### Install Node.js If you don't have Node.js 14+: import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; Using Homebrew: ```bash brew install node ``` Or using nvm (recommended): ```bash # Install nvm curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash # Install latest LTS nvm install --lts nvm use --lts ``` Using NodeSource repository (recommended): ```bash # Node.js 20.x LTS curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash - sudo apt-get install -y nodejs ``` ```bash # Node.js 20.x LTS curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash - sudo dnf install -y nodejs ``` Download from [nodejs.org](https://nodejs.org/) or use chocolatey: ```bash choco install nodejs ``` Or use nvm-windows: ```bash nvm install lts nvm use lts ``` ### Install Dependencies Create a `package.json` file: ```json { "name": "pulsar-simple-client", "version": "1.0.0", "description": "Simplified WebSocket client for Apache Pulsar", "main": "pulsar-simple-client.js", "bin": { "pulsar-simple-client": "./pulsar-simple-client.js" }, "dependencies": { "ws": "^8.18.0" }, "engines": { "node": ">=14.0.0" } } ``` Then install dependencies: ```bash npm install ``` ## Complete Example ```javascript #!/usr/bin/env node /** * Pulsar Simple Client - A simplified WebSocket client for Apache Pulsar */ const WebSocket = require('ws'); const { URL } = require('url'); /** * Pulsar Simple Client class */ class PulsarSimpleClient { constructor(options) { this.host = options.host; this.port = options.port; this.tenant = options.tenant; this.namespace = options.namespace; this.topic = options.topic; this.xApiKey = options.xApiKey; } /** * Build the Pulsar WebSocket URL */ buildUrl() { const baseUrl = `wss://${this.host}:${this.port}/ws/v2/reader/persistent/${this.tenant}/${this.namespace}/${this.topic}`; const url = new URL(baseUrl); // URL query parameter values MUST be URL-encoded. URLSearchParams handles this // for you -- do NOT build query strings with template literals/string concatenation. // Pulsar message IDs in particular contain '+', '/' and '=' characters that // have special meaning in a URL and will silently break a naive query string. if (this.xApiKey) { url.searchParams.set('x-api-key', this.xApiKey); } return url.toString(); } /** * Get HTTP headers for authentication */ getHeaders() { const headers = {}; // Add X-API-Key header if provided if (this.xApiKey) { headers['X-API-Key'] = this.xApiKey; } return headers; } /** * Check if message is a control message (should not be acknowledged) */ isControlMessage(msg) { return (msg.type && msg.type !== '') || (msg.endOfTopic && msg.endOfTopic !== ''); } /** * Parse Pulsar message and extract decoded payload */ extractPulsarPayload(data) { try { const msg = JSON.parse(data); const payloadB64 = msg.payload || ''; // Decode base64 payload const payloadBuffer = Buffer.from(payloadB64, 'base64'); const payload = payloadBuffer.toString('utf-8'); return { msg, payload }; } catch (err) { console.error(`Error extracting payload: ${err.message}`, { stream: process.stderr }); return null; } } /** * Send an acknowledgment message for a received message * This is REQUIRED for WebSocket readers to prevent backlog buildup and message delivery stoppage */ sendAck(ws, messageId) { try { const ackMsg = { messageId }; ws.send(JSON.stringify(ackMsg)); } catch (err) { console.error(`Warning: Failed to send ack: ${err.message}`); } } /** * Connect to WebSocket and receive messages */ async connectAndReceive() { const wsUrl = this.buildUrl(); const headers = this.getHeaders(); console.error(`Connecting to ${wsUrl}...`); // Configure WebSocket options const wsOptions = { headers: headers, }; const ws = new WebSocket(wsUrl, wsOptions); // Handle connection open ws.on('open', () => { console.error('Successfully connected'); }); // Handle incoming messages ws.on('message', (data) => { const result = this.extractPulsarPayload(data.toString()); if (result) { const { msg, payload } = result; // Check if this is a control message (don't print or ack these) if (this.isControlMessage(msg)) { return; } // Print only the payload (suitable for piping) console.log(payload); // Send acknowledgment (required for WebSocket flow control) this.sendAck(ws, msg.messageId); } }); // Handle errors ws.on('error', (err) => { console.error(`WebSocket error: ${err.message}`); process.exit(1); }); // Handle connection close ws.on('close', (code, reason) => { console.error(`Connection closed: ${code} ${reason}`); process.exit(0); }); // Handle process termination process.on('SIGINT', () => { console.error('Interrupted by user'); ws.close(); process.exit(0); }); process.on('SIGTERM', () => { console.error('Terminated'); ws.close(); process.exit(0); }); } } /** * Parse command-line arguments */ function parseArgs() { const args = process.argv.slice(2); // Example uses euw2 region. For other regions, see developer.8x8.com const options = { host: 'pulsar-ws-euw2.8x8.com', port: 443, namespace: 'event-v1', topic: 'all', }; for (let i = 0; i < args.length; i++) { const arg = args[i]; const nextArg = args[i + 1]; switch (arg) { case '--host': options.host = nextArg; i++; break; case '--port': options.port = parseInt(nextArg, 10); i++; break; case '--tenant': options.tenant = nextArg; i++; break; case '--namespace': options.namespace = nextArg; i++; break; case '--topic': options.topic = nextArg; i++; break; case '--x-api-key': options.xApiKey = nextArg; i++; break; } } // Validate required parameters if (!options.tenant) { console.error('Error: --tenant is required'); process.exit(1); } return options; } /** * Main entry point */ async function main() { try { const options = parseArgs(); const client = new PulsarSimpleClient(options); await client.connectAndReceive(); } catch (err) { console.error(`Error: ${err.message}`); process.exit(1); } } // Run main function if (require.main === module) { main(); } module.exports = { PulsarSimpleClient }; ``` ## Running the Client ### Basic Usage ```bash node pulsar-simple-client.js \ --tenant YOUR_TENANT \ --x-api-key YOUR_API_KEY ``` ### Command-Line Options | Option | Description | Default | Required | |---------------|-------------------------------------|---------------------------------------------------|----------| | `--tenant` | Your 8x8 tenant name | - | Yes | | `--host` | Pulsar broker hostname | `pulsar-ws-euw2.8x8.com` (EUW2) | No | | `--port` | Pulsar broker port | `443` | No | | `--namespace` | Pulsar namespace | `event-v1` | No | | `--topic` | Topic name | `all` | No | | `--x-api-key` | API key for authentication | - | No | | `--insecure` | Skip TLS certificate verification | `true` | No | ## Output and Piping The client outputs only the decoded message payload to stdout, with status messages going to stderr. This makes it perfect for piping: ### Pretty-print with jq ```bash node pulsar-simple-client.js --tenant YOUR_TENANT --x-api-key YOUR_KEY | jq . ``` ### Filter events ```bash node pulsar-simple-client.js --tenant YOUR_TENANT --x-api-key YOUR_KEY | \ jq 'select(.eventType == "agent.login")' ``` ### Save to file ```bash node pulsar-simple-client.js --tenant YOUR_TENANT --x-api-key YOUR_KEY > events.log ``` ### Search with grep ```bash node pulsar-simple-client.js --tenant YOUR_TENANT --x-api-key YOUR_KEY | grep "error" ``` ## Key Features ### Modern JavaScript The Node.js client uses modern JavaScript features: ```javascript // Async/await async function connectAndReceive(url, apiKey) { // ... } // Arrow functions ws.on('message', (data) => { // ... }); // Template literals const url = `wss://${host}:${port}/ws/v2/reader/persistent/${tenant}/${namespace}/${topic}`; ``` ### Payload Decoding Messages are decoded in two steps: ```javascript // 1. Parse Pulsar message JSON const pulsarMsg = JSON.parse(data.toString()); // 2. Decode base64 payload const payload = Buffer.from(pulsarMsg.payload, 'base64').toString('utf-8'); ``` ### Message Acknowledgement Acknowledgements are required to prevent message delivery stoppage: ```javascript const ackMsg = { messageId }; ws.send(JSON.stringify(ackMsg)); ``` ### Control Message Filtering Control messages (like end-of-topic markers) should not be processed: ```javascript if (msg.type || msg.endOfTopic) { return; } ``` ### Clean Output - **stdout**: Only decoded message payloads - **stderr**: Status messages and errors This separation ensures piped output remains clean. ### Graceful Shutdown The client handles Ctrl+C gracefully: ```javascript process.on('SIGINT', () => { console.error('Disconnecting...'); ws.close(); }); ``` ## Dependencies The client requires: ```json { "dependencies": { "ws": "^8.16.0" } } ``` Install with: ```bash npm install ``` ## Making the Script Executable On Unix-like systems: ```bash chmod +x pulsar-simple-client.js # Run without node prefix ./pulsar-simple-client.js --tenant YOUR_TENANT --x-api-key YOUR_KEY ``` Or install globally: ```bash npm install -g . # Run from anywhere pulsar-simple-client --tenant YOUR_TENANT --x-api-key YOUR_KEY ``` ## Using npm Scripts Add to `package.json`: ```json { "scripts": { "start": "node pulsar-simple-client.js" } } ``` Then run: ```bash npm start -- --tenant YOUR_TENANT --x-api-key YOUR_KEY ``` ## Error Handling The client handles common errors: - **Connection errors**: WebSocket connection failures - **JSON parse errors**: Malformed Pulsar messages - **Base64 decode errors**: Invalid payload encoding - **SSL/TLS errors**: Certificate verification issues - **Acknowledgement failures**: Issues sending acks (logged as warnings) All errors are logged to stderr without affecting stdout output. ## Troubleshooting ### Module not found: 'ws' Install dependencies: ```bash npm install ``` ### SSL Certificate Errors The `--insecure` flag is enabled by default for testing. For production: ```bash node pulsar-simple-client.js --tenant YOUR_TENANT --x-api-key YOUR_KEY --insecure false ``` ### Connection Refused - Verify hostname and port - Check network connectivity - Ensure firewall allows outbound connections ### Node.js Version Too Old Check your Node.js version: ```bash node --version ``` Requires Node.js 14 or higher. ### Syntax Errors If you see syntax errors, your Node.js version may be too old. Modern JavaScript features require Node.js 14+. ## Comparison with Other Clients | Feature | Node.js | Python | Go | |----------------------|---------------------|---------------------|---------------------| | Language | JavaScript | Python | Go | | Async Model | Async/await | Async/await | Goroutines | | Dependencies | ws library | websockets library | gorilla/websocket | | Build Required | No | No | Yes | | Package Manager | npm | pip | go mod | | Best For | Backend services | Data pipelines | High performance | ## Next Steps - [Python Client Example](./python.mdx) - Python implementation - [Go Client Example](./golang.md) - Go implementation - [Message Format](../message-format.mdx) - Understanding message structure - [Troubleshooting](../troubleshooting.md) - Common issues and solutions --- ## Python Client Example This page provides a complete Python implementation for connecting to the 8x8 Event Streaming service. ## Overview The Python client example demonstrates: - Async/await WebSocket connection using the `websockets` library - Authentication using X-API-Key - Message reading and payload decoding - Command-line argument parsing with argparse - Clean, pipe-friendly output for integration with tools like `jq` ## Prerequisites - **Python 3.7 or higher** (includes required `asyncio` support) - **pip** package manager - **websockets library** (version 12.0 or higher) ## Installation ### Install Python If you don't have Python 3.7+: import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ```bash # Using Homebrew brew install python3 ``` ```bash sudo apt update sudo apt install python3 python3-pip python3-venv ``` ```bash sudo dnf install python3 python3-pip ``` Download from [python.org](https://www.python.org/downloads/) or use chocolatey: ```bash choco install python3 ``` ### Set Up Virtual Environment (Recommended) ```bash # Create virtual environment python3 -m venv venv # Activate virtual environment # On macOS/Linux: source venv/bin/activate # On Windows: venv\Scripts\activate ``` ### Install Dependencies Create a `requirements.txt` file: ```text websockets>=12.0,<14.0 ``` Then install: ```bash pip install -r requirements.txt ``` Or install directly: ```bash pip install 'websockets>=12.0,<14.0' ``` ## Complete Example ```python #!/usr/bin/env python3 """ Pulsar Simple Client - A simplified WebSocket client for Apache Pulsar """ import argparse import asyncio import base64 import json import logging import sys import traceback from urllib.parse import urlencode, urlparse import websockets class PulsarSimpleClient: """Simple WebSocket client for Apache Pulsar reader endpoints""" def __init__(self, host, port, tenant, namespace, topic, x_api_key=None): self.host = host self.port = port self.tenant = tenant self.namespace = namespace self.topic = topic self.x_api_key = x_api_key def build_url(self): """Build the Pulsar WebSocket URL""" base_url = f"wss://{self.host}:{self.port}/ws/v2/reader/persistent/{self.tenant}/{self.namespace}/{self.topic}" # URL query parameter values MUST be URL-encoded. urlencode() handles this # for you -- do NOT build query strings with f-strings/string concatenation. # Pulsar message IDs in particular contain '+', '/' and '=' characters that # have special meaning in a URL and will silently break a naive query string. if self.x_api_key: query_params = {"x-api-key": self.x_api_key} base_url = f"{base_url}?{urlencode(query_params)}" return base_url def get_headers(self): """Build HTTP headers for authentication""" headers = {} # Add X-API-Key header if provided if self.x_api_key: headers["X-API-Key"] = self.x_api_key return headers def is_control_message(self, msg): """Check if message is a control message (should not be acknowledged)""" return msg.get("type") or msg.get("endOfTopic") def extract_pulsar_payload(self, data): """Parse Pulsar message and extract decoded payload""" try: msg = json.loads(data) payload_b64 = msg.get("payload", "") # Decode base64 payload payload_bytes = base64.b64decode(payload_b64) payload = payload_bytes.decode("utf-8") return msg, payload except (json.JSONDecodeError, KeyError, base64.binascii.Error) as e: logging.error(f"Error extracting payload: {e}") return None, None async def send_ack(self, websocket, message_id): """ Send an acknowledgment message for a received message. This is REQUIRED for WebSocket readers to prevent backlog buildup and message delivery stoppage. """ try: ack_msg = {"messageId": message_id} await websocket.send(json.dumps(ack_msg)) except Exception as e: logging.warning(f"Failed to send ack: {e}") async def connect_and_receive(self): """Connect to WebSocket and receive messages""" ws_url = self.build_url() headers = self.get_headers() logging.info(f"Connecting to {ws_url}...") try: async with websockets.connect( ws_url, extra_headers=headers, ssl=True, # Use default SSL verification ping_interval=20, ping_timeout=10 ) as websocket: logging.info("Successfully connected") # Read messages continuously async for message in websocket: msg, payload = self.extract_pulsar_payload(message) if msg and payload: # Check if this is a control message (don't print or ack these) if self.is_control_message(msg): continue # Print only the payload (suitable for piping) print(payload, flush=True) # Send acknowledgment (required for WebSocket flow control) await self.send_ack(websocket, msg.get("messageId")) except websockets.exceptions.WebSocketException as e: logging.error(f"WebSocket error: {type(e).__name__}: {e}") logging.error(traceback.format_exc()) sys.exit(1) except Exception as e: logging.error(f"Error: {type(e).__name__}: {e}") logging.error(traceback.format_exc()) sys.exit(1) def main(): """Main entry point""" parser = argparse.ArgumentParser( description="Simplified WebSocket client for Apache Pulsar" ) # Connection parameters # Example uses euw2 region. For other regions, see developer.8x8.com parser.add_argument( "--host", default="pulsar-ws-euw2.8x8.com", help="Pulsar broker hostname (default: pulsar-ws-euw2.8x8.com)" ) parser.add_argument( "--port", type=int, default=443, help="Pulsar broker port (default: 443)" ) parser.add_argument( "--tenant", required=True, help="Pulsar tenant name (required)" ) parser.add_argument( "--namespace", default="event-v1", help="Pulsar namespace (default: event-v1)" ) parser.add_argument( "--topic", default="all", help="Pulsar topic name (default: all)" ) parser.add_argument( "--x-api-key", help="X-API-Key header value" ) args = parser.parse_args() # Configure logging (to stderr so it doesn't interfere with piped output) logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s", stream=sys.stderr ) # Create client and connect client = PulsarSimpleClient( host=args.host, port=args.port, tenant=args.tenant, namespace=args.namespace, topic=args.topic, x_api_key=args.x_api_key ) # Run the async client try: asyncio.run(client.connect_and_receive()) except KeyboardInterrupt: logging.info("Interrupted by user") sys.exit(0) if __name__ == "__main__": main() ``` ## Running the Client ### Basic Usage ```bash python3 pulsar_simple_client.py \ --tenant YOUR_TENANT \ --x-api-key YOUR_API_KEY ``` ### Command-Line Options | Option | Description | Default | Required | |---------------|-------------------------------------|---------------------------------------------------|----------| | `--tenant` | Your 8x8 tenant name | - | Yes | | `--host` | Pulsar broker hostname | `pulsar-ws-euw2.8x8.com` (EUW2) | No | | `--port` | Pulsar broker port | `443` | No | | `--namespace` | Pulsar namespace | `event-v1` | No | | `--topic` | Topic name | `all` | No | | `--x-api-key` | API key for authentication | - | No | | `--insecure` | Skip TLS certificate verification | `true` | No | ## Output and Piping The client outputs only the decoded message payload to stdout, with status messages going to stderr. This makes it perfect for piping: ### Pretty-print with jq ```bash python3 pulsar_simple_client.py --tenant YOUR_TENANT --x-api-key YOUR_KEY | jq . ``` ### Filter events ```bash python3 pulsar_simple_client.py --tenant YOUR_TENANT --x-api-key YOUR_KEY | \ jq 'select(.eventType == "agent.login")' ``` ### Save to file ```bash python3 pulsar_simple_client.py --tenant YOUR_TENANT --x-api-key YOUR_KEY > events.log ``` ### Search with grep ```bash python3 pulsar_simple_client.py --tenant YOUR_TENANT --x-api-key YOUR_KEY | grep "error" ``` ## Key Features ### Async/Await Design The Python client uses modern async/await syntax for efficient WebSocket handling: ```python async with websockets.connect(url, additional_headers=headers, ssl=ssl_context) as websocket: async for message in websocket: # Process message pass ``` ### Payload Decoding Messages are decoded in two steps: ```python # 1. Parse Pulsar message JSON pulsar_msg = json.loads(message) # 2. Decode base64 payload payload = base64.b64decode(pulsar_msg['payload']).decode('utf-8') ``` ### Message Acknowledgement Acknowledgements are required to prevent message delivery stoppage: ```python ack_msg = {"messageId": message_id} await websocket.send(json.dumps(ack_msg)) ``` ### Control Message Filtering Control messages (like end-of-topic markers) should not be processed: ```python if msg.get("type") or msg.get("endOfTopic"): continue ``` ### Clean Output - **stdout**: Only decoded message payloads - **stderr**: Status messages and errors This separation ensures piped output remains clean. ## Dependencies The client requires: ```txt websockets>=12.0 ``` Install with: ```bash pip install -r requirements.txt ``` ## Virtual Environment Using a virtual environment is recommended to avoid dependency conflicts: ```bash # Create and activate python3 -m venv venv source venv/bin/activate # macOS/Linux # venv\Scripts\activate # Windows # Install dependencies pip install -r requirements.txt # Run client python3 pulsar_simple_client.py --tenant YOUR_TENANT --x-api-key YOUR_KEY # Deactivate when done deactivate ``` ## Making the Script Executable ```bash chmod +x pulsar_simple_client.py # Run without python3 prefix ./pulsar_simple_client.py --tenant YOUR_TENANT --x-api-key YOUR_KEY ``` ## Error Handling The client handles common errors: - **Connection errors**: WebSocket connection failures - **JSON parse errors**: Malformed Pulsar messages - **Base64 decode errors**: Invalid payload encoding - **SSL/TLS errors**: Certificate verification issues - **Acknowledgement failures**: Issues sending acks (logged as warnings) All errors are logged to stderr without affecting stdout output. ## Troubleshooting ### ImportError: No module named 'websockets' Install dependencies: ```bash pip install -r requirements.txt ``` ### SSL Certificate Errors The `--insecure` flag is enabled by default for testing. For production: ```bash python3 pulsar_simple_client.py --tenant YOUR_TENANT --x-api-key YOUR_KEY --insecure False ``` ### Connection Refused - Verify hostname and port - Check network connectivity - Ensure firewall allows outbound connections ### Python Version Too Old Check your Python version: ```bash python3 --version ``` Requires Python 3.7 or higher. ## Next Steps - [Node.js Client Example](./nodejs.mdx) - JavaScript/Node.js implementation - [Go Client Example](./golang.md) - Go implementation - [Message Format](../message-format.mdx) - Understanding message structure - [Troubleshooting](../troubleshooting.md) - Common issues and solutions --- ## Field Reference(Streaming) This page provides detailed documentation for all fields that appear in event payloads, including field value codes, transformations, and the comprehensive attachedData keys reference. import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ## Comprehensive AttachedData Keys Reference The `attachedData` field can contain a wide variety of keys depending on the interaction type, channel, and system configuration. Below is a comprehensive reference organized by category: ### Basic Interaction Fields | Key | Type | Description | |---------------|--------|----------------------------------------------------------| | `phoneNum` | number | Primary phone number for the interaction | | `pho` | mixed | Phone number (short key) | | `callingName` | mixed | Caller's name or calling number | | `callerId` | number | Caller ID number | | `@pri` | number | Priority value | | `priority` | number | Queue priority (50 is standard) | | `med` | string | Media type code"T" for phone"C" for chat/SMS | | `cha` | mixed | Channel identifier | | `channelName` | mixed | Channel/queue name | | `cnt` | number | Counter field | | `con` | number | Contact/record ID | | `tok` | number | Token identifier | ### Queue and Routing Fields | Key | Type | Description | |-------------------|--------|-------------------------------------------------------| | `que` | string | Queue identifier (format: `tenant~~queue~~media~~id`) | | `oqu` | string | Original queue before transfer | | `queueDirection` | string | "in" (inbound)"out" (outbound) | | `tenantName` | string | Tenant name | | `tenantSkillName` | string | Skill/queue name | ### Timing Fields | Key | Type | Description | |----------------------------------|--------|--------------------------------------------| | `tim` | number | Timestamp (seconds since epoch) | | `otim` | number | Original/queue entry time (seconds) | | `waitTime` | number | Wait time in seconds | | `callAnsweredTime` | number | Call answered timestamp (milliseconds) | | `callHangupTime` | number | Call hangup timestamp (milliseconds) | | `callAnsweredTenantTT` | string | Answered time formatted in tenant timezone | | `callHangupTenantTT` | string | Hangup time formatted in tenant timezone | | `callDuration` | string | Call duration formatted (HH:MM:SS) | | `callDurationSec` | number | Call duration in seconds | | `phoneQueueInteractionStartTime` | number | Phone queue interaction start (seconds) | ### Recording Fields | Key | Type | Description | |-------------------|--------|------------------------------------------------------| | `tenantRecServer` | string | Recording server name (e.g., "eu10nfs01") | | `aAgtCallRec` | string | Agent call recording setting"yes""no" | | `agtInitVR` | string | Agent initiated recording"NotSet"etc. | | `callerPerm` | string | Caller permission for recording"yes""no" | | `vRecP` | number | Recording parameter | ### Outbound and Campaign Fields | Key | Type | Description | |--------------------------|---------|-------------------------------------------------------------| | `ctl_transType` | string | Transaction type"agentdial""campaign"etc. | | `destType` | string | Destination type"external"etc. | | `outboundTclItemId` | mixed | Outbound transaction code list item ID | | `outboundTclListId` | mixed | Outbound transaction code list ID | | `outboundTclShortCode` | string | Outbound TCL short code | | `campId` | number | Campaign ID | | `campName` | string | Campaign name | | `dialMode` | string | Dial mode"preview""progressive"etc. | | `extRecordId` | number | External record ID | | `extRecordType` | string | External record type"customer"etc. | | `phoneList` | string | Phone list for campaign | | `previewTimeout` | string | Preview timeout setting | | `rejectOnPreviewTimeout` | boolean | Whether to reject on preview timeout | | `retry_count` | number | Retry count for outbound call | | `showSkipButton` | boolean | Whether skip button is shown | ### Transfer and Original Interaction Fields | Key | Type | Description | |---------------------------|--------|----------------------------------------------| | `originalInteractionGuid` | string | GUID of original interaction before transfer | | `originalTokenId` | number | Token ID of original interaction | | `originalTransactionId` | number | Transaction ID of original interaction | ### Transaction Codes and Disposition | Key | Type | Description | |--------------------|--------|--------------------------------------------------------------------------------------------------------------------------------------------------------| | `tclSelectedItems` | string | Selected transaction codes in format "listId\|itemId,listId\|itemId" - see [Transaction Codes](#transaction-codes-and-disposition) section for details | ### Digital Channel Fields (Chat, SMS, Email) | Key | Type | Description | |-----------------------------------|---------|----------------------------------------------| | `chaletChat` | boolean | Whether this is a Chalet chat | | `chaletRoom` | string | Chalet room identifier | | `channelId` | string | Channel ID (short form) | | `createInteractionTimestampNanos` | number | Creation timestamp in nanoseconds | | `customerLanguage` | string | Customer language code (e.g., "en") | | `extTransactionData` | string | External transaction data (formatted string) | | `extTransactionDataID` | string | External transaction data ID | | `sessionId` | string | Session identifier | | `timezoneOffset` | number | Timezone offset | | `type` | string | Interaction type (e.g., "SMS") | | `nam` | string | Name field for digital channels | ### Remote Participant Fields | Key | Type | Description | |---------------------|--------|---------------------------------------------------| | `remoteCallingName` | string | Remote caller name (formatted) | | `remotePhoneNum` | string | Remote phone number (formatted with country code) | ### Advanced/System Fields | Key | Type | Description | |----------------------------|--------|------------------------------------| | `ctl_userdata` | string | Control user data | | `externalUserData` | string | External user data string | | `carrierCallBlockingCheck` | number | Carrier call blocking check result | | `record_phone_index` | number | Phone record index | | `schema_user` | string | Database schema user | **Note:** Not all keys will be present in every event. The keys present depend on: - Media type (phone, chat, email, SMS) - Interaction direction (inbound vs outbound) - System configuration and enabled features - Customer-specific customizations ## Field Values Reference Many fields in event payloads use numeric ordinal values to represent states, modes, and types. This section documents what these numeric values mean. ### Resource Type Codes The `resourceType` field indicates the media channel type: | Code | Media Type | Description | |------|------------|-------------------------------| | 0 | Phone | Voice call interaction | | 1 | Email | Email interaction | | 2 | Chat | Chat or messaging interaction | | 3 | SMS | SMS text message interaction | | 4 | Video | Video call interaction | **Example:** ```json { "resourceType": 0, "mediaType": "phone" } ``` **Note:** The `mediaType` field provides a normalized string representation ("phone", "email", "chat"), while `resourceType` contains the numeric code. ### Recording Mode Codes The `recordingMode` field indicates the recording state: | Code | Mode | Description | |------|---------|--------------------------------------------------| | 0 | Off | Recording is disabled | | 1 | Record | Recording is active | | 2 | Monitor | Monitoring mode (recording without notification) | **In JSON, these appear as strings:** ```json { "recordingMode": "yes", // Corresponds to code 1 (Record) "recordingMode": "no" // Corresponds to code 0 (Off) } ``` ### Call State Codes The `callState` field indicates the current state of an interaction throughout its lifecycle: | Code | Description | Typical Events | |-------------------|------------------------------------------------------------------------|--------------------------------------------------| | `CS_IDLE` | Initial state - interaction created but not yet queued | InteractionCreated | | `CS_QUEUED` | Interaction waiting in queue to be assigned to an agent | InteractionQueued, InteractionUnqueued | | `CS_INPROGRESS` | Interaction assigned to agent but not yet connected (ringing/alerting) | InteractionAssigned | | `CS_CONNECTED` | Active interaction - agent and customer are connected | InteractionAccepted, InteractionCustomerAccepted | | `CS_HOLD` | Interaction is on hold | LineHoldStatus (status: true) | | `CS_DISCONNECTED` | Interaction has ended - one or more parties disconnected | InteractionDeleted, InteractionPostProcess | **State Transitions:** Typical interaction lifecycle for inbound calls: ```mermaid stateDiagram-v2 [*] --> CS_IDLE CS_IDLE --> CS_QUEUED CS_QUEUED --> CS_INPROGRESS CS_INPROGRESS --> CS_CONNECTED CS_CONNECTED --> CS_HOLD: Agent places on hold CS_HOLD --> CS_CONNECTED: Agent takes off hold CS_CONNECTED --> CS_DISCONNECTED CS_DISCONNECTED --> [*] ``` Typical interaction lifecycle for outbound calls: ```mermaid stateDiagram-v2 direction LR [*] --> CS_IDLE CS_IDLE --> CS_INPROGRESS CS_INPROGRESS --> CS_CONNECTED CS_CONNECTED --> CS_DISCONNECTED CS_DISCONNECTED --> [*] ``` **Example in events:** ```json { "event": "InteractionQueued", "callState": "CS_QUEUED", "queueTime": 1744819217 } ``` ```json { "event": "InteractionAccepted", "callState": "CS_CONNECTED", "agentId": "agAglVJkg0TU28dok9y9UQKg" } ``` **Note:** The `callState` field appears in most Interaction events and is essential for tracking interaction progress through the system. ### Agent State Codes Agent-related events use numeric codes for states and presence: #### Login State | Code | State | Description | |------|------------|---------------------| | 0 | LOGGED_OUT | Agent is logged out | | 1 | LOGGED_IN | Agent is logged in | #### Presence | Code | Presence | Description | |------|----------|-----------------------------------| | 0 | Offline | Agent is offline | | 1 | Online | Agent is online and available | | 2 | DND | Do Not Disturb (busy/unavailable) | **Example from AgentStatusChange event:** ```json { "newState": 5, // Current agent state "newSubState": "none", // Sub-state description "newSubStateReason": "none" } ``` ### Reject Reason Codes The `rejectReason` field (in `InteractionRejected` events) indicates why an interaction was rejected: | Code | Reason | Description | |------|--------------|-------------------------------------------| | 0 | Unknown | Rejection reason not specified | | 1 | Timeout | Agent did not respond in time | | 2 | Agent Reject | Agent explicitly rejected the interaction | ### Hangup Reason Codes The `callHangupReason` field (in `InteractionDeleted` events) indicates why a call ended. This provides detailed information about call termination beyond just who initiated the hangup. | Code | Category | Description | Common Scenarios | |-----------------------------|-----------|-------------------------------------------------------|------------------------------------------| | `CEC_NONE` | Normal | No specific reason provided | Default value, generic termination | | `CEC_DISCONNECT_NORMAL` | Normal | Normal call termination - call completed successfully | Customer and agent finished conversation | | `CEC_DISCONNECT_BUSY` | Failed | Called number was busy | Outbound call to busy number | | `CEC_DISCONNECT_BADADDRESS` | Failed | Invalid phone number or unreachable destination | Malformed number, disconnected number | | `CEC_DISCONNECT_NOANSWER` | Failed | Called party did not answer | Outbound call rang but no answer | | `CEC_DISCONNECT_CANCELLED` | Cancelled | Call was cancelled before connection | Agent or customer cancelled during ring | | `CEC_DISCONNECT_REJECTED` | Rejected | Call was rejected by the called party or system | Customer/system declined call | | `CEC_DISCONNECT_FAILED` | Failed | Call failed due to system or network error | Network issues, system errors | | `CEC_DISCONNECT_BLOCKED` | Blocked | Call was blocked (e.g., by spam filter, DNC list) | Compliance blocking, spam detection | **Usage with hangupInitiator:** The `callHangupReason` works in conjunction with `hangupInitiator` to provide complete termination information: | hangupInitiator | callHangupReason | Interpretation | |-----------------|---------------------------|--------------------------------------------| | `CUSTOMER` | `CEC_DISCONNECT_NORMAL` | Customer hung up normally | | `AGENT` | `CEC_DISCONNECT_NORMAL` | Agent hung up normally | | `SYSTEM` | `CEC_DISCONNECT_NOANSWER` | System terminated - customer didn't answer | | `SYSTEM` | `CEC_DISCONNECT_FAILED` | System terminated - technical failure | | `SYSTEM` | `CEC_DISCONNECT_BLOCKED` | System terminated - call blocked | **Example in InteractionDeleted event:** ```json { "event": "InteractionDeleted", "callState": "CS_DISCONNECTED", "hangupInitiator": "SYSTEM", "callHangupReason": "CEC_DISCONNECT_NOANSWER", "dispositionCode": 1000 } ``` **Interpretation:** System terminated the call because the customer didn't answer. **Analytics Use Cases:** - **Call completion rates**: Count `CEC_DISCONNECT_NORMAL` vs other reasons - **Technical issues**: Monitor `CEC_DISCONNECT_FAILED` for system problems - **Contact rates**: Track `CEC_DISCONNECT_NOANSWER` and `CEC_DISCONNECT_BUSY` for outbound campaigns - **Compliance monitoring**: Track `CEC_DISCONNECT_BLOCKED` for regulatory compliance ## Field Transformations The system performs several transformations on field values between the internal event format and the output you receive. Understanding these transformations helps explain why certain fields have specific formats. ### GUID Extraction Agent IDs are stored internally as GUIDs with the format `"X-AgentId-Y"`, but are extracted to simpler agent IDs for output: **Internal format:** `"tenant01-agAglVJkg0TU28dok9y9UQKg-e46672cd-660c-46f5-b8f0-856eaf18f65c"` **Extracted agentId:** `"agAglVJkg0TU28dok9y9UQKg"` The extraction process: 1. GUID is split by `-` delimiter 2. The component starting with `ag` is extracted 3. This becomes the `agentId` field value 4. The full GUID is preserved in `agentGUID` field **Example in event:** ```json { "agentGUID": "tenant01-agAglVJkg0TU28dok9y9UQKg-e46672cd-660c-46f5-b8f0-856eaf18f65c", "agentId": "agAglVJkg0TU28dok9y9UQKg" } ``` ### Queue ID Parsing Queue identifiers are stored internally using a `~~` (double-tilde) separated format: `"tenant~~queue~~media~~id"`, but are parsed to extract just the numeric queue ID: **Internal format:** `"tenant01~~1162~~phone~~b1e3e2f2-b01a-4a03-ba3d-f30859414d97"` **Extracted queueId:** `1162` The parsing process: 1. Split the string by `~~` delimiter 2. Extract the numeric queue ID component 3. This becomes the `queueId` field value **Example in attachedData:** ```json { "attachedData": { "attachedDatum": [ {"attachedDataKey": "que", "attachedDataValue": "tenant01~~1162~~phone~~..."} ] }, "queueId": 1162 } ``` ### Media Type Normalization The `mediaType` field is normalized from numeric `resourceType` codes to human-readable strings: | resourceType | mediaType | |--------------|-----------| | 0 | "phone" | | 1 | "email" | | 2 | "chat" | | 3 | "chat" | | 4 | "phone" | **Example:** ```json { "resourceType": 0, "mediaType": "phone" } ``` ### Participating Agents Array Parsing The `participatingAgents` field contains semicolon-separated agent IDs that are parsed from internal GUID format: **Internal format:** `"tenant01-agAglVJkg0TU28dok9y9UQKg-guid1;tenant01-agBbcDef123-guid2"` **Parsed output:** `"agAglVJkg0TU28dok9y9UQKg,agBbcDef123"` (comma-separated agent IDs) **Note:** In some events, this may appear as comma-separated instead of semicolon-separated, depending on the transformation stage. ### JSON Types Are Not Fixed Per Field {#boolean-string-representation} For compatibility with the legacy Streaming API, a field's JSON type is not guaranteed to be the same in every message. Do not bind event fields to a fixed type, and parse defensively. Two separate behaviours cause this. **1. Values that look like a boolean or a number lose their quotes** A text value is emitted without quotes whenever the value itself reads as a JSON boolean or number. This is decided per value, not per field, so the same field can arrive as a string in one message and as a boolean or number in the next: | Underlying value | Appears in the event as | |------------------|-------------------------| | `resume` | `"resume"` | | `true` | `true` | | `0` | `0` | | `0123` | `"0123"` | A leading zero keeps its quotes, as `0123` is not a valid JSON number. This is why boolean-like fields vary between representations: ```json { "isAgentInitiated": false, // JSON boolean in some contexts "isOutboundCall": true, // JSON boolean "isDirectAccess": "false", // String in other contexts "isExternal": "true" // String } ``` **2. Some fields mean different things in different events** A field's type can also depend on which event carries it. `status` is the clearest example: | Event | `status` | |------------------------------------|-------------------------------------------------| | `LineHoldStatus`, `LineMuteStatus` | Boolean - `true` or `false` | | `RecordingStatus` | Text - `"pause"`, `"resume"` and similar values | A consumer that assumes `status` is always boolean will fail when a `RecordingStatus` event arrives with `"resume"`. Check the `event` field before interpreting values whose meaning depends on it. **Handling this** - Accept both quoted and unquoted forms for any field that can be boolean-like or numeric - for example, treat `true` and `"true"` as the same value. - Read values as text first and convert afterwards, rather than binding directly to a boolean or numeric type. - Do not let one unparseable value stop your consumer. If you use the Consumer API and a message fails to deserialize without being acknowledged, it will be redelivered and your client can loop on the same message. ### Timestamp Formats Events include timestamps in multiple formats: | Field Name | Format | Example | Description | |----------------------|-------------------------------|---------------|----------------------------------------------------------------------------------------| | `interactionEventTS` | Seconds since epoch (integer) | 1744819274 | When this event occurred. Not present on every event type (see below) | | `eventTS` | Seconds since epoch (integer) | 1744819217 | When the interaction was offered (entered the queue). Does not advance as events occur | | `queueTime` | Seconds since epoch (integer) | 1744819217 | Time entered queue | | `msgInfo.timestamp` | Milliseconds since epoch | 1744819274183 | When 8x8 published the message. Present on every event | ### Choosing a Timestamp The two event-level timestamps answer different questions and are not interchangeable: - **`interactionEventTS` — when the event happened.** Use this to order events and to measure durations between them. - **`eventTS` — when the interaction was offered.** This describes the interaction, not the event, so every event for a given interaction carries the same value. Subtracting one event's `eventTS` from another's yields zero. Use it only when you want the offer time itself. It is also absent from many events, including most `InteractionCreated` events, so do not rely on it being present. - **`msgInfo.timestamp` — when 8x8 published the message.** Present on every event type without exception, and in milliseconds. Use it for staleness and freshness checks. The `publishTime` in the Pulsar message metadata serves the same purpose. **Events without an event timestamp** `interactionEventTS` is not present on every event. It is absent from: | Event type | Carries | |--------------------|-------------------------| | `GuestChatEnd` | `eventTS` only | | `AgentUpdate` | No timestamp of its own | | `AgentLoginUpdate` | No timestamp of its own | | `AgentProvChange` | No timestamp of its own | Handle the absence explicitly rather than letting it default. A missing value treated as `0` decodes to 1 January 1970, which makes a current event appear arbitrarily old. Use `msgInfo.timestamp` for these event types. **Resolution** `interactionEventTS`, `eventTS` and `statusEventTS` are whole seconds, and the value is truncated rather than rounded. A duration derived from them can be up to one second longer than the true figure, so they are not suitable for sub-second latency measurement. Where that precision matters, use `msgInfo.timestamp`, which is in milliseconds. **Converting to Date/Time:** ```go // From seconds - the time the event occurred. interactionEventTS is absent on // some event types, where it unmarshals to 0; fall back to the publish time // rather than decoding 1970. eventTime := time.UnixMilli(event.MsgInfo.Timestamp) if event.InteractionEventTS > 0 { eventTime = time.Unix(event.InteractionEventTS, 0) } // From milliseconds - the time 8x8 published the message msgTime := time.UnixMilli(event.MsgInfo.Timestamp) ``` ```java // From seconds - the time the event occurred. interactionEventTS is absent on // some event types, so fall back to the publish time rather than decoding 1970. Long eventTS = event.getInteractionEventTS(); Instant eventTime = (eventTS != null && eventTS > 0) ? Instant.ofEpochSecond(eventTS) : Instant.ofEpochMilli(event.getMsgInfo().getTimestamp()); // From milliseconds - the time 8x8 published the message Instant msgTime = Instant.ofEpochMilli(event.getMsgInfo().getTimestamp()); ``` ```python from datetime import datetime # From seconds - the time the event occurred. interactionEventTS is absent on # some event types; treat missing, null or empty as absent and fall back to the # publish time rather than decoding 1970. event_ts = event.get('interactionEventTS') if event_ts: event_time = datetime.fromtimestamp(event_ts) else: event_time = datetime.fromtimestamp(event['msgInfo']['timestamp'] / 1000) # From milliseconds - the time 8x8 published the message msg_time = datetime.fromtimestamp(event['msgInfo']['timestamp'] / 1000) ``` ```javascript // From seconds - the time the event occurred. interactionEventTS is absent on // some event types; treat missing, null or empty as absent and fall back to the // publish time rather than decoding 1970. const eventTime = event.interactionEventTS ? new Date(event.interactionEventTS * 1000) : new Date(event.msgInfo.timestamp); // From milliseconds - the time 8x8 published the message const msgTime = new Date(event.msgInfo.timestamp); ``` ## Transaction Codes and Disposition Interaction events related to call wrap-up include two important but distinct fields for tracking call outcomes: ### dispositionCode vs transactionCodeList #### dispositionCode A **single primary outcome code** representing the overall call result: - Simple string or numeric value - High-level categorization of what happened - Examples: `"CALLBACK"`, `"SALE"`, `"NO_ANSWER"`, `1000` - Always a single value - Appears in `InteractionDeleted` and `InteractionEndPostProcess` events **Example:** ```json { "event": "InteractionDeleted", "dispositionCode": 1000, "agentNotes": "Customer requested callback tomorrow" } ``` #### transactionCodeList **Multiple detailed transaction codes** for granular categorization: - Structured format: `"listId|itemId,listId|itemId"` - Can contain multiple codes for different aspects of the interaction - References database-defined Transaction Code Lists (TCL) - Used for detailed analytics, compliance tracking, and business intelligence - Agents can select multiple codes during wrap-up - System may auto-apply mandatory codes **Format:** Comma-separated pairs of `listId|itemId` **Example:** ```json { "attachedData": { "attachedDatum": [ {"attachedDataKey": "tclSelectedItems", "attachedDataValue": "5|12,6|45,8|99"} ] } } ``` This translates to: - List 5, Item 12: "Product Type - Insurance" - List 6, Item 45: "Customer Sentiment - Satisfied" - List 8, Item 99: "Follow-up Required - Yes" ### Complete Example A typical wrap-up scenario showing both fields: ```json { "event": "InteractionDeleted", "dispositionCode": "SALE", "agentNotes": "Sold premium insurance package", "attachedData": { "attachedDatum": [ {"attachedDataKey": "tclSelectedItems", "attachedDataValue": "5|12,6|45,8|99"} ] } } ``` Here: - **dispositionCode** = `"SALE"` (overall outcome: sale was made) - **transactionCodeList** = `"5|12,6|45,8|99"` (detailed categorization: insurance product, satisfied customer, follow-up needed) ### Configuration Transaction Code Lists are configured by administrators in **8x8 Configuration Manager**: #### Creating Transaction Code Lists 1. Navigate to: **Configuration Menu → Transaction Codes → Add** 2. Configure properties for the code list (name, description, settings) 3. Define individual codes in the **Codes** tab 4. Optional: Translate codes to secondary languages #### Viewing List and Item IDs To understand the numeric IDs in `tclSelectedItems`: - **List ID**: In the Transaction Codes list, select dropdown → **Columns → ID** to show the list ID column - **Item ID**: Edit a list → **Codes** tab → Select dropdown → **Columns → ID** to show individual transaction code IDs These numeric IDs (listId and itemId) are what appear in `tclSelectedItems` as `"listId|itemId"`. #### Assignment - Transaction code lists are assigned to **agent groups** or **queues** - Multiple transaction code lists can be applied to a single agent group or queue - Agents only see codes from lists assigned to their group/queue #### Agent Selection - Agents select transaction codes during interaction wrap-up in Agent Console/Workspace - Selected codes are recorded in the `tclSelectedItems` field - System may automatically apply mandatory codes if configured ### Documentation References - [Transaction codes overview](https://docs.8x8.com/8x8WebHelp/VCC/configuration-manager-vovcc/content/transactioncodespageoverview.htm) - [Create transaction codes](https://docs.8x8.com/8x8WebHelp/VCC/configuration-manager-vovcc/content/creatingtransactioncodelist.htm) - [Agent: Select transaction codes](https://docs.8x8.com/8x8WebHelp/contact-center/agent-workspace/Content/transaction-codes.htm) ## UserData Extraction The `attachedData` object contains an array of key-value pairs. Many of these keys are extracted from a nested internal structure called `userData` and transformed into top-level fields or remain in `attachedData`. ### Extraction Mapping The following table shows how nested `userData` fields are extracted and mapped: | userData Key | Output Field | Description | Example Value | |-----------------------------|------------------------------------------|------------------------------------------------------|---------------------| | `userData.otim` | `eventTS` | Original/queue entry timestamp (seconds since epoch) | 1744819217 | | `userData.tclSelectedItems` | attachedData: `tclSelectedItems` | Transaction code list (format: listId\|itemId,...) | "5\|12,6\|45,8\|99" | | `userData.queueDirection` | `direction` | Queue direction (inbound/outbound) | "in", "out" | | `userData.cha` | `inboundChannelid` | Inbound channel identifier | 441733968848 | | `userData.que` | `queueId` | Queue ID (parsed from `~~` separated format) | 1162 | | `userData.tim` | `queueTime` | Time when interaction entered queue (seconds) | 1744819217 | | `userData.ema` | `emailSourceAddress` | Email source address (for email interactions) | `user@example.com` | | `userData.aAgtCallRec` | `overrideAutoInteractionRecording` | Override auto recording setting | "yes", "no" | | `userData.agtCallRec` | `allowAgentOverrideInteractionRecording` | Allow agent to override recording | "yes", "no" | | `userData.callerPerm` | `callerPermission` | Caller permission for recording | "yes", "no" | ### How Extraction Works 1. **Internal Event Creation**: The system creates an internal event with nested `userData` structure 2. **Field Extraction**: Specific keys from `userData` are extracted to top-level fields or separate `attachedData` entries 3. **Format Transformation**: Some fields undergo additional transformation (e.g., queue ID parsing from `~~` format) 4. **Preservation**: The complete `userData` structure is also preserved in `attachedData` for reference ### Example **Internal userData structure:** ```text userData = { "otim": "1744819217", "que": "tenant01~~1162~~phone~~b1e3e2f2-b01a-4a03-ba3d-f30859414d97", "queueDirection": "in", "cha": "441733968848", "tclSelectedItems": "5|12,6|45" } ``` **Extracted to output event:** ```json { "eventTS": 1744819217, "queueId": 1162, "direction": "in", "inboundChannelid": 441733968848, "attachedData": { "attachedDatum": [ {"attachedDataKey": "que", "attachedDataValue": "tenant01~~1162~~phone~~..."}, {"attachedDataKey": "queueDirection", "attachedDataValue": "in"}, {"attachedDataKey": "cha", "attachedDataValue": 441733968848}, {"attachedDataKey": "tclSelectedItems", "attachedDataValue": "5|12,6|45"} ] } } ``` **Note:** - `eventTS` is extracted from `userData.otim` - `queueId` is extracted and parsed from `userData.que` (removing the `~~` format) - `direction` is extracted from `userData.queueDirection` - `inboundChannelid` is extracted from `userData.cha` - Original values are preserved in `attachedData` for reference ## Next Steps - [Message Format](./message-format.mdx) - Learn how to decode and process messages - [Event Reference](./event-reference.md) - Browse all available event types - [Code Examples](./examples/golang.md) - See field usage in context --- ## Getting Started(Streaming) ## Prerequisites Required: 1. **Your 8x8 tenant name** 2. **API credentials** - See [Authentication](./authentication.mdx) for details - **Recommended:** **Admin Console Keys** from **8x8 Admin Console** (X-API-Key format) - **Also supported:** **Contact Center (CC) Tokens** from **Configuration Manager** 3. **A WebSocket client** - Use provided examples or your own implementation ## Quick Start with Example Clients Use one of the pre-built client examples: > 📘 **Authentication** > > The examples below use `--x-api-key` which works for both **Admin Console Keys** (recommended) and **CC Tokens** (also supported). See the [Authentication Guide](./authentication.mdx) for details on obtaining credentials. > > import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; See the [Go Client Example](./examples/golang.md) for complete code and build instructions. See the [Java Client Example](./examples/java.md) for complete code and Maven configuration. See the [Python Client Example](./examples/python.mdx) for complete code and dependencies. See the [Node.js Client Example](./examples/nodejs.mdx) for complete code and package.json. Access the [Pulsar UI](./examples/browser.md) web service - no installation required. ## What You'll See Events are delivered in JSON format: ```json { "eventId": "evt_123456", "eventType": "agent.login", "timestamp": "2025-10-15T09:30:00Z", "tenantId": "your-tenant", "data": { "agentId": "12345", "extensionId": "67890" } } ``` ## Processing Events Pipe output to tools like `jq` for filtering and processing: ```bash # Pretty-print all events ./pulsar-simple-client -tenant YOUR_TENANT -x-api-key YOUR_KEY | jq . # Filter only agent login events ./pulsar-simple-client -tenant YOUR_TENANT -x-api-key YOUR_KEY | \ jq 'select(.eventType == "agent.login")' # Extract specific fields ./pulsar-simple-client -tenant YOUR_TENANT -x-api-key YOUR_KEY | \ jq '{eventType: .eventType, timestamp: .timestamp}' ``` ## Connection Parameters The quick start client requires your **tenant name** and **API key**. The service connects to: - **Host**: Regional endpoint (e.g., `pulsar-ws-euw2.8x8.com` for UK3) - see [Regional Endpoints](./connection.md#regional-endpoints) - **Namespace**: `event-v1` - **Topic**: `all` (receives all event types) For complete connection details including all available topics, query parameters, and URL construction, see the [Connection Guide](./connection.md). ## Next Steps - **[Connection details](./connection.md)** - WebSocket connection details - **[Authentication options](./authentication.mdx)** - Authentication methods - **[Message format](./message-format.mdx)** - Event structure - **[Code examples](./examples/golang.md)** - Complete working examples - **[Troubleshooting](./troubleshooting.md)** - Common issues ## Need Help? For issues: 1. See [Troubleshooting Guide](./troubleshooting.md) 2. Review the complete code examples in the language-specific guides above 3. Contact 8x8 support for credentials or access issues --- ## Message Format Structure of messages received from the 8x8 Event Streaming service. import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; ## Pulsar WebSocket Message Structure Messages received via the WebSocket connection follow the Pulsar WebSocket message format. Each message is a JSON object with the following structure: ```json { "messageId": "CAEQAQ==", "payload": "eyJldmVudElkIjogImV2dF8xMjM0NTYiLCAiZXZlbnRUeXBlIjogImFnZW50LmxvZ2luIn0=", "properties": {}, "publishTime": "2025-10-15T09:30:00.000Z", "redeliveryCount": 0 } ``` ### Field Descriptions | Field | Type | Description | |-------------------|--------|------------------------------------------------------------| | `messageId` | string | Unique identifier for this message (base64 encoded) | | `payload` | string | The actual event data (base64 encoded JSON) | | `properties` | object | Optional metadata key-value pairs | | `publishTime` | string | Timestamp when the message was published (ISO 8601 format) | | `redeliveryCount` | number | Number of times this message has been redelivered | ## Payload Decoding The `payload` field contains the actual event data, encoded in base64. To access the event data: 1. **Base64 decode** the payload string 2. **Parse the result** as UTF-8 JSON ## Event Payload Structure After decoding the base64 payload, you'll receive a JSON object representing the event. Events follow a structured format with common fields and event-specific data. ### Common Event Fields Most events include these common fields: | Field | Type | Description | |----------------------|--------|--------------------------------------------------------------| | `msgInfo` | object | Metadata about the event (instanceId, sequenceId, timestamp) | | `msgInfo.instanceId` | string | Server instance identifier | | `msgInfo.sequenceId` | number | Event sequence number for ordering | | `msgInfo.timestamp` | number | Event timestamp in milliseconds since epoch | **Note:** The `msgInfo` object provides important context about the event source and sequencing. ### Interaction Event Fields Interaction events (phone calls, chats, etc.) include additional common fields: | Field | Type | Description | |-----------------------|---------|--------------------------------------------------------------------------------------------------| | `event` | string | Event type name (e.g., "InteractionCreated") | | `interactionGUID` | string | Unique identifier for this interaction | | `interactionEventTS` | number | When this event occurred (seconds since epoch). Absent on some event types | | `callState` | string | Current call state (see Call States table below) | | `mediaType` | string | Communication channel"phone""chat""email"etc. | | `direction` | string | Interaction direction"in" (inbound)"out" (outbound) | | `attachedData` | object | Additional metadata as key-value pairs | | `resourceType` | number | Numeric resource type identifier | | `agentId` | string | Agent identifier (when agent is involved) | | `agentGUID` | string | Full agent GUID (when agent is involved) | | `participatingAgents` | string | Comma-separated list of participating agent IDs | | `queueId` | number | Queue identifier | | `queueList` | number | Queue list identifier | | `queueTime` | number | Time when interaction entered queue (seconds since epoch) | | `eventTS` | number | When the interaction was offered (seconds since epoch). Same on every event for that interaction | | `transactionNum` | number | Transaction number | | `recordingMode` | string | Recording mode ("yes", "no") | | `isAgentInitiated` | boolean | Whether interaction was initiated by agent | | `isDirectAccess` | boolean | Whether this is direct access interaction | | `isExternal` | boolean | Whether interaction involves external party | | `inboundChannelid` | mixed | Inbound channel identifier (number or "N/A" for outbound) | | `isOutboundCall` | boolean | Whether this is an outbound call | For detailed information about specific fields, see the [Field Reference](./field-reference.mdx). ### Call States Interaction events use these call state values: | Call State | Description | |-------------------|----------------------------------------------| | `CS_IDLE` | Call created, not yet in progress | | `CS_QUEUED` | Call waiting in queue for an available agent | | `CS_INPROGRESS` | Call in progress (ringing or connecting) | | `CS_CONNECTED` | Call connected and active | | `CS_HOLD` | Call on hold (music playing) | | `CS_DISCONNECTED` | Call ended or hung up | For comprehensive call state documentation including transitions, see [Field Reference - Call State Codes](./field-reference.mdx#call-state-codes). ## Event Lifecycle and Correlation Events for an interaction are correlated using the `interactionGUID` field. To understand how events flow through an interaction's lifecycle, including state transitions, timing, and patterns for different call types, see the [Event Lifecycle Guide](./event-lifecycle.mdx). **Key points for event correlation:** - Use `interactionGUID` to group all events for a single interaction - Track `callState` to monitor state transitions - Calculate durations using `interactionEventTS`, not `eventTS` (see [Choosing a Timestamp](./field-reference.mdx#choosing-a-timestamp)) - Check `hangupInitiator` and `callHangupReason` in InteractionDeleted to understand outcomes ## Attached Data Structure The `attachedData` field contains an array of key-value pairs: ```json "attachedData": { "attachedDatum": [ {"attachedDataKey": "phoneNum", "attachedDataValue": 5551234567}, {"attachedDataKey": "callingName", "attachedDataValue": "Alpaca Herd"}, {"attachedDataKey": "channelName", "attachedDataValue": "Support Queue"} ] } ``` Common attached data keys include: - `phoneNum`: Phone number - `callingName`: Caller name - `channelName`: Channel/queue name - `queueDirection`: Queue direction (in/out) - `tenantSkillName`: Skill name - `@pri`: Priority value For a comprehensive list of all attachedData keys, see the [Field Reference](./field-reference.mdx#comprehensive-attacheddata-keys-reference). ## Message Types The WebSocket connection can receive several types of frames: ### Text and Binary Messages Most messages will be **TextMessage** or **BinaryMessage** types containing the Pulsar message JSON. ### Control Messages - **CloseMessage**: Indicates the server is closing the connection - **PingMessage**: Heartbeat from server (respond with PongMessage) - **PongMessage**: Response to PingMessage Most WebSocket libraries handle ping/pong automatically. ## Processing Messages ### Basic Processing Pattern 1. **Receive WebSocket message** 2. **Parse the Pulsar message JSON** to extract the payload field 3. **Base64 decode the payload** 4. **Parse the decoded payload as JSON** to get the actual event 5. **Process the event** based on its type and data ### Example: Complete Message Processing ```go // Read WebSocket message messageType, message, err := conn.ReadMessage() if err != nil { return err } // Parse Pulsar message var pulsarMsg PulsarMessage if err := json.Unmarshal(message, &pulsarMsg); err != nil { return err } // Decode payload payload, err := base64.StdEncoding.DecodeString(pulsarMsg.Payload) if err != nil { return err } // Parse event var event Event if err := json.Unmarshal(payload, &event); err != nil { return err } // Process event processEvent(event) ``` ```java @Override public void onMessage(String message) { try { // Parse Pulsar message PulsarMessage pulsarMsg = objectMapper.readValue(message, PulsarMessage.class); // Decode payload byte[] payloadBytes = Base64.getDecoder().decode(pulsarMsg.getPayload()); String payloadJson = new String(payloadBytes, StandardCharsets.UTF_8); // Parse event Event event = objectMapper.readValue(payloadJson, Event.class); // Process event processEvent(event); } catch (Exception e) { log.error("Error processing message: {}", e.getMessage()); } } ``` ```python import json import base64 async def process_websocket_message(message): try: # Parse Pulsar message pulsar_msg = json.loads(message) # Decode payload payload_bytes = base64.b64decode(pulsar_msg['payload']) payload_json = payload_bytes.decode('utf-8') # Parse event event = json.loads(payload_json) # Process event process_event(event) except (json.JSONDecodeError, KeyError, UnicodeDecodeError) as e: print(f"Error processing message: {e}") ``` ```javascript ws.on('message', (data) => { try { // Parse Pulsar message const pulsarMsg = JSON.parse(data.toString()); // Decode payload const payloadBytes = Buffer.from(pulsarMsg.payload, 'base64'); const payloadJson = payloadBytes.toString('utf-8'); // Parse event const event = JSON.parse(payloadJson); // Process event processEvent(event); } catch (e) { console.error('Error processing message:', e.message); } }); ``` ## Message Ordering Messages are delivered in the order they were published to the topic. When reading from `earliest`, you'll receive all available historical messages in order before receiving new messages. ## Event Sequence Examples Understanding the sequence of events for common call scenarios helps when building integrations. The following examples show typical event flows with their `EventType` and `callState` values. ### Inbound Call | Step | Action | Event Type | Call State | |------|--------------------------------------------------------------------|-----------------------------|-------------------| | 1 | An incoming call from a customer | `InteractionCreated` | `CS_QUEUED` | | 2 | The call goes into queue and is waiting to be assigned to an agent | `InteractionQueued` | `CS_QUEUED` | | 3 | The call is assigned to an agent | `InteractionAssigned` | `CS_QUEUED` | | 4 | The agent accepts the call and speaks with the customer | `InteractionAccepted` | `CS_CONNECTED` | | 5 | Automatic post processing starts (conclusion/wrapping up of call) | `InteractionPostProcess` | `CS_DISCONNECTED` | | 6 | Automatic post processing concludes | `InteractionEndPostProcess` | `CS_DISCONNECTED` | | 7 | The customer ends the call | `InteractionDeassigned` | `CS_DISCONNECTED` | | 8 | The agent ends the call | `InteractionDeassigned` | `CS_DISCONNECTED` | ### Outbound Call (No Queue) | Step | Action | Event Type | Call State | |------|---------------------------------------------|-------------------------------|-------------------| | 1 | An outbound call is created | `InteractionCreated` | `CS_IDLE` | | 2 | The call is assigned to an agent | `InteractionAssigned` | `CS_IDLE` | | 3 | System starts recording the call leg | `InteractionRecordingStarted` | N/A | | 4 | Call is in progress, ringing at destination | `InteractionAccepted` | `CS_INPROGRESS` | | 5 | Customer answers the call | `InteractionCustomerAccepted` | `CS_CONNECTED` | | 6 | Call wrap-up process concludes | `InteractionEndPostProcess` | `CS_DISCONNECTED` | | 7 | Call is reassigned from agent | `InteractionDeassigned` | `CS_DISCONNECTED` | ### Call Transfer to Another Agent | Step | Action | Event Type | Call State | |------|--------------------------------------------|--------------------------------|-------------------| | 1 | Original call in progress | `InteractionCreated` | `CS_IDLE` | | 2 | First agent connected with customer | `InteractionAccepted` | `CS_CONNECTED` | | 3 | First agent calls second agent (line 2) | `InteractionCreated` | `CS_IDLE` | | 4 | Second agent's phone rings | `InteractionAssigned` | `CS_IDLE` | | 5 | Second agent answers | `InteractionAccepted` | `CS_INPROGRESS` | | 6 | First agent transfers call to second agent | `InteractionTransferRequest` | N/A | | 7 | First agent's wrap-up starts | `InteractionPostProcess` | `CS_DISCONNECTED` | | 8 | Second agent joins the call | `InteractionParticipantChange` | `CS_CONNECTED` | | 9 | First agent's wrap-up concludes | `InteractionEndPostProcess` | `CS_DISCONNECTED` | | 10 | First agent deassigned | `InteractionDeassigned` | `CS_DISCONNECTED` | > 📘 **Note** > > These are simplified examples. Real call flows may have additional events depending on features used (hold, recording, conferencing, etc.). See [Event Reference](./event-reference.md) for complete event type descriptions. > > ## Sample Event Messages The following examples show real event messages with actual field structures and values. ### Agent Status Change ```json { "AgentStatusChange": { "agentId": "ag64oyEUb_Sk6bxVB9P5yaaa", "msgInfo": { "instanceId": "us1tomcat04.us1.whitepj.net-sapi-v1", "sequenceId": 215, "timestamp": 1669235544917 }, "newReasonCodeUser": "801=1722", "newState": 5, "newSubState": "none", "newSubStateReason": "none", "statusEventTS": 1669235544 } } ``` ### Interaction Created (Incoming Call) ```json { "Interaction": { "attachedData": { "attachedDatum": [ { "attachedDataKey": "@pri", "attachedDataValue": 100 }, { "attachedDataKey": "callingName", "attachedDataValue": "Andrew Cunningh" }, { "attachedDataKey": "cha", "attachedDataValue": 13125555068 }, { "attachedDataKey": "phoneNum", "attachedDataValue": 5515557212 }, { "attachedDataKey": "remoteCallingName", "attachedDataValue": "Andrew Cunningh" }, { "attachedDataKey": "remotePhoneNum", "attachedDataValue": "+15515557212" } ] }, "callState": "CS_QUEUED", "event": "InteractionCreated", "inboundChannelid": 13125555068, "interactionEventTS": 1669235827, "interactionGUID": "int-184a63564dc-ohWfVIbHJz2Hr2JFhAfdlb4Fa-phone-00-acmecorp01", "msgInfo": { "instanceId": "us1tomcat04.us1.whitepj.net-sapi-v1", "sequenceId": 224, "timestamp": 1669235827942 }, "resourceType": 0 } } ``` ### Interaction Queued ```json { "Interaction": { "attachedData": { "attachedDatum": [ { "attachedDataKey": "channelName", "attachedDataValue": "Acme Ads OG" }, { "attachedDataKey": "que", "attachedDataValue": "acmecorp01~~queue~~phone~~591" }, { "attachedDataKey": "queueDirection", "attachedDataValue": "in" }, { "attachedDataKey": "priority", "attachedDataValue": 50 }, { "attachedDataKey": "tenantSkillName", "attachedDataValue": "Test Sales" } ] }, "callState": "CS_QUEUED", "direction": "in", "event": "InteractionQueued", "eventTS": 1669235836, "inboundChannelid": 13125555068, "interactionEventTS": 1669235836, "interactionGUID": "int-184a63564dc-ohWfVIbHJz2Hr2JFhAfdlb4Fa-phone-00-acmecorp01", "isAgentInitiated": false, "mediaType": "phone", "msgInfo": { "instanceId": "us1tomcat04.us1.whitepj.net-sapi-v1", "sequenceId": 225, "timestamp": 1669235836708 }, "priority": 50, "queueId": 591, "queueList": 591, "queueTime": 1669235836, "resourceType": 0, "transactionNum": 19727 } } ``` ### Interaction Deleted (Call Ended) ```json { "Interaction": { "attachedData": { "attachedDatum": [ { "attachedDataKey": "callingName", "attachedDataValue": "Andrew Cunningh" }, { "attachedDataKey": "phoneNum", "attachedDataValue": 5515557212 }, { "attachedDataKey": "remotePhoneNum", "attachedDataValue": "+15515557212" } ] }, "callHangupReason": "CEC_DISCONNECT_NORMAL", "callState": "CS_DISCONNECTED", "dispositionCode": 1000, "event": "InteractionDeleted", "hangupInitiator": "CUSTOMER", "inboundChannelid": 13125555068, "interactionEventTS": 1669236911, "interactionGUID": "int-184a645e074-zGIsMxOkCYzQ8yg68e3t61i7A-phone-00-acmecorp01", "isAgentInitiated": false, "isDirectAccess": false, "isQueued": false, "mediaType": "phone", "msgInfo": { "instanceId": "us1tomcat04.us1.whitepj.net-sapi-v1", "sequenceId": 251, "timestamp": 1669236911188 }, "recordingMode": "no", "rejectReason": 0, "resourceType": 0 } } ``` ## Next Steps - [Event Reference](./event-reference.md) - Browse all available event types - [Field Reference](./field-reference.mdx) - Detailed field documentation - [Code Examples](./examples/golang.md) - See complete message processing examples - [Troubleshooting](./troubleshooting.md) - Common message processing issues --- ## Migration Guide This guide helps you transition from the legacy Streaming API (SAPI) to the new Apache Pulsar-based Event Streaming service. ## Migration Options All existing customers will be automatically migrated to the 8x8 Event Streaming service. If you would like to take a more direct path or access additional features before your migration date, there are two options: ### Option 1: Full Migration (Recommended) Migrate to the new Pulsar API with native WebSocket protocol. **Benefits:** - **Best performance**: Lower latency, higher throughput - **New features**: Access to Consumer API, Reader API, and future enhancements - **Full flexibility**: Choose between Reader (simple streaming) or Consumer (subscriptions with acknowledgements) - **No connection limits**: Scale to as many connections as needed - **Agent-specific subscriptions**: Subscribe to individual agents via `agent-v1` topics for targeted monitoring - **Cloud-native**: Built on Apache Pulsar infrastructure **What's required:** - Update endpoint URL - Add Pulsar message wrapper handling (base64 decode) **Endpoint:** `wss://pulsar-ws-{region}.8x8.com/ws/v2/reader/...` (see [Regional Endpoints](./connection.md#regional-endpoints)) **Documentation:** - [Getting Started](./getting-started.mdx) - Quick start guide with complete examples - [Connection Guide](./connection.md) - WebSocket connection details and available topics - [Message Format](./message-format.mdx#payload-decoding) - How to decode Pulsar message wrappers - [Code Examples](./examples/golang.md) - Working implementations in Go, Java, Python, Node.js, and Browser ### Option 2: Adapter Migration Switch to the backwards-compatible adapter endpoint with no code changes. The adapter is intended for existing integrations that cannot move to Pulsar directly — new integrations should use Option 1. **Benefits:** - **No code changes**: Existing clients work without modification - **Cloud infrastructure**: Benefit from new Pulsar backend reliability and scalability - **Easy transition**: Change URL only **What's required:** - Update endpoint URL only **Endpoint:** `wss://vcc-sapi-bridge-{region}.8x8.com/...` — for example, UK3 uses `wss://vcc-sapi-bridge-euw2.8x8.com/...` The adapter is deployed in the same regions as the Pulsar API and uses the same `{region}` suffixes — see [Regional Endpoints](./connection.md#regional-endpoints) for the region list. The URL path is unchanged from the legacy Streaming API, so existing clients only need the hostname replaced. ## What Happens If You Do Nothing Your integration will be automatically migrated to the new platform via a backwards-compatible adapter. No action is required on your side — your existing code will continue to work. You will receive advance notice with your migration date before this happens. ## Migration Timeline Existing customers will be migrated to the new platform on a rolling schedule, with advance notice sent before each cluster migration. Your integration will continue to work automatically via a backwards-compatible adapter — no code changes required. If you would like to take advantage of the full platform before your migration date, see Options 1 and 2 above. Regardless of your migration date: - **New features** will only be available on the Pulsar-based platform - **Performance improvements** are focused on the new infrastructure - **Long-term support** is committed to the Pulsar-based service ## Related Resources - [Legacy Streaming API Documentation](../legacy-streaming-api-overview.md) - [Apache Pulsar Documentation](https://pulsar.apache.org/docs/) *Apache Pulsar is a trademark of the Apache Software Foundation.* --- ## Overview(Streaming) 8x8 Event Streaming provides a real-time stream of events from your 8x8 platform. ## What is Event Streaming? The service delivers events as they occur, including: - Agent login/logout events - Call state changes - Queue updates - Interaction events - Agent status changes Event Streaming supports custom integrations, dashboards, and automation tools that respond to events in real-time. ## When to Use This API Event Streaming is designed for real-time event processing and advanced integration scenarios. Consider this API when: - **Real-time event streams are required** - You need immediate notification of contact center events as they occur - **Advanced use cases** - Standard APIs like CCA Realtime, CCA Historical, or CEX Recent Calls cannot meet your requirements - **Server-to-server integration** - You're building cloud-to-cloud or server-to-server integrations ## Architecture The 8x8 Event Streaming service is built on [Apache Pulsar](https://pulsar.apache.org/), an open-source distributed messaging and streaming platform. We expose Pulsar's WebSocket interface, which provides: - **Real-time streaming**: Events are delivered as they occur - **Reliable delivery**: Built on Pulsar's proven messaging infrastructure - **Simple integration**: Standard WebSocket protocol supported by all major languages - **Scalability**: Handles high-volume event streams efficiently ## Key Features - **WebSocket-based**: Uses standard WebSocket protocol for broad compatibility - **Multi-language support**: Client examples available in Go, Java, Python, Node.js, and Browser (HTML/JavaScript) - **Reader and Consumer APIs**: Both Pulsar Reader API (simple streaming) and Consumer API (with subscriptions and acknowledgements) are supported - **Flexible positioning**: Start reading from earliest, latest, or a specific message ## Migrating from Legacy Streaming API If you're currently using the legacy Streaming API (SAPI), this Apache Pulsar-based service is the recommended platform for all new integrations and future development. - **Automatic migration**: Existing customers will be automatically migrated to the new platform on a rolling schedule - **Recommended for all new integrations**: New customers should use this Pulsar-based API - **Better performance and reliability**: Improved scalability, message durability, and connection stability - **Future features**: New capabilities and enhancements will only be available on this platform See the [Migration Guide](./migration.md) for detailed information on transitioning from the legacy API. ## Getting Started To consume events from 8x8 Event Streaming: 1. **[Set up authentication](./authentication.mdx)** - Configure API credentials 2. **[Establish a connection](./connection.md)** - Connect to the WebSocket endpoint 3. **[Process messages](./message-format.mdx)** - Parse and handle incoming events 4. **[Review examples](./examples/golang.md)** - Working code samples ## Service Availability The 8x8 Event Streaming service is available in multiple AWS regions worldwide: - **Protocol**: WebSocket Secure (WSS) - **Regional Endpoints**: See [Regional Endpoints](./connection.md#regional-endpoints) for hostname mapping Connect to the endpoint that corresponds to your 8x8 Contact Center deployment region. ## Next Steps - [Getting Started Guide](./getting-started.mdx) - Quick start - [Connection Guide](./connection.md) - Connection details - [Code Examples](./examples/golang.md) - Code samples ## Related Resources - [Apache Pulsar Documentation](https://pulsar.apache.org/docs/) - [Apache Pulsar WebSocket API](https://pulsar.apache.org/docs/client-libraries-websocket/) *Apache Pulsar is a trademark of the Apache Software Foundation.* --- ## Troubleshooting(Streaming) Common issues when connecting to the 8x8 Event Streaming service. ## Connection Issues ### Cannot Connect to WebSocket **Symptoms:** - Connection timeout - Connection refused error - Network unreachable **Solutions:** 1. **Verify the hostname and port:** ```bash # Test basic connectivity ping pulsar-ws-euw2.8x8.com # Test port accessibility nc -zv pulsar-ws-euw2.8x8.com 443 ``` 2. **Check firewall rules:** - Ensure outbound HTTPS (port 443) is allowed - Check corporate proxy settings - Verify VPN is not blocking the connection 3. **Verify DNS resolution:** ```bash nslookup pulsar-ws-euw2.8x8.com ``` ### TLS/SSL Certificate Errors **Symptoms:** - Certificate verification failed - x509: certificate signed by unknown authority - SSL handshake failed **Solutions:** 1. **Update root certificates:** ```bash # macOS brew upgrade ca-certificates # Ubuntu/Debian sudo apt-get update && sudo apt-get install ca-certificates ``` 2. **For development only - skip verification:** ```bash # Go ./pulsar-client -insecure -tenant YOUR_TENANT -x-api-key YOUR_KEY ``` :::danger Never use `-insecure` or `InsecureSkipVerify` in production! ::: ### Connection Timeout **Symptoms:** - Connection hangs - Timeout after 45 seconds **Solutions:** 1. **Check network latency:** ```bash # Test network latency curl -w "@-" -o /dev/null -s https://pulsar-ws-euw2.8x8.com <<'EOF' time_connect: %{time_connect}s\n time_total: %{time_total}s\n EOF ``` 2. **Increase timeout values:** - Set connection timeout to 60+ seconds for slow networks - Check if there are network issues between your location and the service ### Wrong Regional Endpoint **Symptoms:** - Connection succeeds but no events received - Authorization succeeds but no data - Empty message stream **Solutions:** 1. **Verify you're using the correct regional endpoint:** - Check your Contact Center deployment region - See [Regional Endpoints](./connection.md#regional-endpoints) for hostname mapping - Example: If your Contact Center is in UK3, use `pulsar-ws-euw2.8x8.com` 2. **Confirm your region with 8x8 Support** if unsure ## Authentication Issues ### 401 Unauthorized **Symptoms:** - HTTP 401 status code - "Unauthorized" error message **Solutions:** 1. **Verify your API key:** - Check for typos or extra whitespace - Ensure you're using the correct API key for your tenant 2. **Check header format:** ```go // Recommended (conventional casing) headers.Set("X-API-Key", "your-api-key") // Also works (HTTP headers are case-insensitive) headers.Set("x-api-key", "your-api-key") // Wrong headers.Set("API-Key", "your-api-key") // Missing X- ``` 3. **Verify tenant name:** - Ensure tenant name matches your 8x8 organization - Check for typos in the tenant parameter ### 403 Forbidden **Symptoms:** - HTTP 403 status code - "Forbidden" error message **Solutions:** 1. **Verify API key permissions:** - Your API key may be valid but lack necessary permissions - Contact 8x8 support to verify your access level 2. **Check topic access:** - Ensure you have permission to read from the specified topic - Try the default topic `all` first ## Message Processing Issues ### Cannot Decode Payload **Symptoms:** - Base64 decode errors - "illegal base64 data" error **Solutions:** 1. **Verify payload extraction:** ```go // Correct - extract payload field from Pulsar message var pulsarMsg PulsarMessage json.Unmarshal(message, &pulsarMsg) payload, err := base64.StdEncoding.DecodeString(pulsarMsg.Payload) // Incorrect - trying to decode entire message payload, err := base64.StdEncoding.DecodeString(string(message)) ``` 2. **Check for truncated messages:** - Ensure you're reading the complete WebSocket message - Check buffer sizes and read loops ### Invalid JSON in Payload **Symptoms:** - JSON parse errors after decoding payload - Unexpected data structure **Solutions:** 1. **Pretty-print to inspect:** ```bash ./pulsar-client -tenant YOUR_TENANT -x-api-key YOUR_KEY | jq . | head -20 ``` 2. **Log the decoded payload:** ```go payload, _ := pulsarMsg.DecodePayload() log.Printf("Decoded payload: %s", string(payload)) ``` ### No Messages Received **Symptoms:** - Successfully connected but no messages appear - Empty output **Solutions:** 1. **Check starting position:** - By default, starts from `latest` (only new messages) - Try starting from `earliest` to see historical messages: ```bash # Using the client from the examples ./pulsar-simple-client -tenant YOUR_TENANT -x-api-key YOUR_KEY -message-id earliest ``` 2. **Verify topic has messages:** - Check with 8x8 support that events are being published - Ensure your tenant is configured for event streaming 3. **Generate test events:** - Perform actions that trigger events (e.g., agent login) - Check if these events appear in the stream ## Performance Issues ### High Memory Usage **Solutions:** 1. **Reduce receiver queue size:** - Lower the `receiverQueueSize` parameter - Process messages faster to avoid buffering 2. **Limit message history:** - Start from `latest` instead of `earliest` - Process messages in batches ### Slow Message Processing **Solutions:** 1. **Optimize JSON parsing:** - Use efficient JSON parsers - Consider streaming JSON parsers for large messages 2. **Parallelize processing:** - Process messages in goroutines (Go) or threads (Java) - Use worker pools for CPU-intensive operations ## Debugging Tips ### Enable Verbose Logging **Go:** ```go log.SetFlags(log.LstdFlags | log.Lshortfile) log.SetOutput(os.Stderr) ``` **Java:** ```xml ``` ### Inspect WebSocket Traffic Use tools like `wscat` or browser developer tools: ```bash # Install wscat npm install -g wscat # Connect manually wscat -c "wss://pulsar-ws-euw2.8x8.com/ws/v2/reader/persistent/YOUR_TENANT/event-v1/all?x-api-key=YOUR_KEY" ``` ### Check Process Output Redirect stderr and stdout separately: ```bash ./pulsar-client -tenant YOUR_TENANT -x-api-key YOUR_KEY 1>output.log 2>errors.log ``` ## Getting Help If you're still experiencing issues: 1. **Collect diagnostic information:** - Error messages and stack traces - Connection parameters (without API key!) - Network environment details - Client version information 2. **Review example code:** - [Go Client Example](./examples/golang.md) - [Java Client Example](./examples/java.md) - [Node.js Client Example](./examples/nodejs.mdx) - [Python Client Example](./examples/python.mdx) - [Browser UI](./examples/browser.md) 3. **Contact support:** - Reach out to 8x8 support with diagnostic information - Provide specific error messages and reproduction steps ## Common Error Messages | Error Message | Likely Cause | Solution | |-----------------------------------|-----------------------------|------------------------------------| | `connection refused` | Wrong host/port or firewall | Verify hostname and check firewall | | `401 Unauthorized` | Invalid credentials | Check API key and tenant name | | `403 Forbidden` | Insufficient permissions | Contact support for access | | `certificate verify failed` | TLS certificate issue | Update root certificates | | `illegal base64 data` | Incorrect payload parsing | Extract payload field first | | `EOF` or `unexpected EOF` | Connection closed | Implement reconnection logic | | `context deadline exceeded` | Timeout | Increase timeout or check network | ## Best Practices 1. **Implement exponential backoff for reconnection** 2. **Log connection states and errors** 3. **Monitor connection health with ping/pong** 4. **Validate messages before processing** 5. **Handle disconnections gracefully** 6. **Keep client libraries up to date** ## Related Resources - [Apache Pulsar Troubleshooting](https://pulsar.apache.org/docs/administration-troubleshooting/) - [WebSocket Protocol Specification](https://datatracker.ietf.org/doc/html/rfc6455) --- ## Theming ## Introduction After configuring your web flow, you may want to be able to change the configuration of your invitation, form, window, while you can do this within configuration manager, this doesn’t give the flexibility to theme all of the areas. Previously, we allow CSS to use this, however this needed the help of a web developer, so this way it makes it available for who even is configuring the web design to build the script. When a colour is entered in, you can either use a word or hex colour, but using the hex colour will give more flexibility. Eg. `red` or `#FF0000` Nearly all of the components on the webchat, have been defined so you can change configurations in each area, however these are only configuration at a global level, within the theme property. To see the glossary of all items you can theme, go to all theming items. However, we are going to first cover some of the main usecases that customers want to cover :::note The widget injects its styles at runtime, so if your website enforces a Content Security Policy it must allow `style-src 'unsafe-inline'` or the widget renders unstyled. See [Content Security Policy](./content-security-policy.md). ::: --- ## Trigger webchat ## Introduction There are occasions, where you might want to trigger the webchat yourself. For example, an action has happened on your webpage, and you want to trigger a set invitation to bring the customer into your chat or where, you want to create your own button your website, and when a customer clicks on this you launch the webchat ### Script config Here is an example of what to set when you want to launch the invitation ```javascript function fn(chatApp) { window.chatApp = chatApp; } ``` Here is an example of what to set when you want to launch the webchat ```javascript window.chatApp.startChat() ``` To add this, you need to add it at the bottom of the script in this area here ```html })( --ADD THE FUNCTION CHATAPP CODE HERE ); ``` Then, the full script will look like this ```html ``` ### --- ## Troubleshooting(Docs) ## Event Log You can access the **Event Log** in **[Configuration Manager](https://docs.8x8.com/8x8WebHelp/VCC/configuration-manager-general/content/cfgoverview.htm) > Integration > Webhooks > Event Log** for issue notification to help you troubleshoot your Chat implementation. ![1790](../images/a6d411f-Screenshot_2021-07-09_at_12.56.14.png "Screenshot 2021-07-09 at 12.56.14.png") In this view, you can see all conversation related requests made to the Chat Gateway API marked as **Inbound**. This view also includes all the requests that 8x8 made to your webhook URL (**[Setup](/actions-events/docs/chat-workflow)**), marked in this view as **Outbound** in **Direction** column. ## Properties The records listed in **Event Log** contain the following properties: | Property | Type | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Direction | Enum: - Outbound- Inbound | This describes the direction of the request from 8x8's perspective: - **Outbound** - 8x8 requests made on your Webhook- **Inbound** - requests you make towards the Chat API | | Status | Status codes according to HTTP protocol, defined in [RFC 2610](https://datatracker.ietf.org/doc/html/rfc2616#section-10). | The value is either the status returned when 8x8 sends you a notification or the status when the Chat API is called (e.g., create conversation, add message). | | Tenant ID | String | The tenant ID provided by 8x8. | | Interaction ID | String | The Interaction ID provides routing information. When a chat interaction is being offered to an agent, it has the reference to the conversation resource, and the agent joins the conversation. This value can be missing on the first event as the interaction is being created after the conversation. You can find it in the events and correlate it with the conversation ID. | | Response | String | The response body | | Timestamp | **`MMM DD, YYYY, HH:MM:SS`** | The request timestamp | | Record Type | Enum: - **`VERIFY_WEBHOOK`**- **`CONVERSATION_UPDATE`**- **`QUEUED`**- **`MEMBERS_CHANGED`**- **`TRANSFER`**- **`MESSAGE`**- **`ACTIVITY`** | The record type contains the operation represented in the event. | | Webhook ID | String | The webhook ID for the conversation. See **[Webhooks](/actions-events/docs/webhooks-2)** | | Channel ID | String | The ID of the Channel that relates to the conversation. See **[Channels](/actions-events/docs/channel)** | | Conversation ID | String | The ID of the conversation, see **[Conversations](/actions-events/docs/conversation)** | | Transaction ID | Number | The Transaction ID is a unique identifier that overlaps with the interaction ID. This one can be used by an agent handling a conversation, if something was wrong and further investigation is recommended. See **[How to get transaction ids in 8x8 Contact Center](https://support.8x8.com/cloud-contact-center/virtual-contact-center/agents/how-to-get-transaction-ids-in-8x8-contact-center)** | | Duration | 00m:00s:00ms | How long the request takes. | | Retry Attempts | Number | The number of Outbound requests that are unsuccessful. The number value indicates the attempted retry. | ### Search You can search for a particular event in the search bar and use the located event to extract information for further review and analysis. ![image](../images/b7b5b45-Screenshot_2021-07-23_at_13.28.49.png "Screenshot 2021-07-23 at 13.28.49.png") ### Filters You can filter using [properties](/actions-events/docs/troubleshooting#properties). To expand the filter view, click on **Advance filter**. Multiple filtering conditions can be applied using **logical operators** such as **`and`** and **`or`** to group them. Conditions in filters are made of **relation operators** such as **`equals`**, **`not equal`**, **greater than**, **`greater than or equal to`**, **`less than`** or **`less than or equal to`**. > 📘 **Note:** > > Record [**properties**](/actions-events/docs/troubleshooting#properties) contain different data types, so not all relation operators apply. > > For example: > > * **enums** - only equals or not equals > * **string** - only equals or not equals > * **number** - all relation operators > --- ## Validating Webhook Events The Webhook events HTTP **`POST`** requests have custom headers containing the digital signature of the event that enable you to establish: * Integrity * Confidentiality * Idempotency * Non-repudiation * Authentication for the event. ## Custom HTTP headers | Header | Description | | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`x-8x8-tenant-id`** | Contains your tenant ID This information matches the information in **[Configuration Manager](https://docs.8x8.com/8x8WebHelp/VCC/configuration-manager-general/content/cfgoverview.htm)** **> Home > Profile > Tenant Label** | | **`x-8x8-event-id`** | The unique ID that is used to determine that the event notification you received is distinct from previous ones. | | **`x-8x8-signature`** | The **[digital signature](/actions-events/docs/validating-webhook-events#signature)** which enables you to establish identity and confirms that the message has not been compromised. If the signature is not valid, the message can be dropped. See **[signature](/actions-events/docs/validating-webhook-events#signature)** for validation information. | | **`x-8x8-transmission-time`** | The header for the request timestamp. This is different from the event timestamp that you may receive in the payload. If you are not able to confirm the event with a 2XX code, then 8x8 will retry the event after a brief delay. The transmission time changes at each retry. | | **`x-8x8-retry`** | Indicates the retry attempt for the same event. | | **`x-8x8-customer-id`** | The unique customer ID. | ## Signature The JSON Web Signature (JWS) with detached content and an unencoded payload. The JWS is as specified according to [RFC 7515](https://datatracker.ietf.org/doc/html/rfc7515) which consists of: * A [JOSE Header](/actions-events/docs/validating-webhook-events#jose-header) * [Data to be Signed](/actions-events/docs/validating-webhook-events#data-to-be-signed) (not present if detached) * The JWS signature value #### JOSE Header The JOSE Header describes the cryptographic operations applied to JWS. The Chat API signature consists of: * **`alg`**: The **`alg`** (algorithm) header parameter identifies the cryptographic algorithm used to secure JWS (**[RFC 7515 Section 4.1.1](https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.1)**) * **`kid`**: The **`kid`** (key ID) header parameter is a hint indicating which key was used to secure JWS (**[RFC 7515 Section 4.1.4](https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.4)**) The **`kid`** is the ID of the keys resource used to sign JWS You can use this kid to fetch the **[public key](/actions-events/reference/getjwkpublickey-1)** and use it to validate JWS * **`b64`**: The **`b64`** header parameter stores password hashes computed with encoding **[RFC 7797 Section 3](https://datatracker.ietf.org/doc/html/rfc7797#section-3)**) Because the payload is not encoded, this value is **false** * **crit**: the **`crit`** (Critical) header parameter **[RFC 7515 Section 4.1.11](https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.11)** This list contains **`b64`** encoding. **[RFC 7797 Section 6](https://datatracker.ietf.org/doc/html/rfc7797#section-6)** #### Data to be signed The unencoded detached payload is in JSON format containing the following properties: | Key | Description | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **`checksum`** long | The checksum of the **`POST`** payload.You must:- Serialize the byte array with UTF-8 encoding- Compute the CRC32 checksum of the encoded bodyThe CRC32 checksum value should be expressed in decimal format. | | **`tt`** long | **Transmission Time** - this value can be obtained from the **x-8x8-transmission-time** header | | **`cid`** string | **Customer ID** - this value can be obtained from the **x-8x8-customer-id** header | | **`tid`** string | **Tenant ID** - this value can be obtained from the **x-8x8-tenant-id** header | | **`eid`** string | **Event ID** - this value can be fetched from the **x-8x8-event-id** header | | **`retry`** long | **Retry attempt** - this value can be fetched from the **x-8x8-retry** header | > 📘 **Note:** > > Since a signature is computed for this payload, the order of keys is of critical importance. Review and adhere to the following order. > > The keys are lexicographically ordered as follows: 1. **`checksum`** 2. **`cid`** 3. **`eid`** 4. **`retry`** 5. **`tid`** 6. **`tt`** For example: ```json { "checksum": 1411811814, "cid": "vccC8ProdChecksUS", "eid": "g4nqGuj8TpCa6tiZ3DeeNw", "retry": 0, "tid": "vccC8ProdChecksUS", "tt": 1629804577296 } ``` The request should resemble the following: ```bash curl --request POST \ --url http://your-webhook-domain-url/callback \ --header 'cache-control: no-cache' \ --header 'content-type: application/json' \ --header 'x-8x8-customer-id: vccC8ProdChecksUS' \ --header 'x-8x8-event-id: g4nqGuj8TpCa6tiZ3DeeNw' \ --header 'x-8x8-retry: 0' \ --header 'x-8x8-signature: eyJiNjQiOmZhbHNlLCJjcml0IjpbImI2NCJdLCJraWQiOiJrZXkxIiwiYWxnIjoiUlMyNTYifQ..O4kXJAvWFtxYZERsJX-OkGLYL__7-rtQrm6y9MFwaISGw1timf1QDQpXy6-8095M67-eN-rUQDNwalktIdHs--DBpR-ratQd1bDlrPMR5CGlsbLFso-KziuqJycBBYmpLIs0JhFihTfoBstduRsQyK-oX0bAu1ZytTVLgmzPkAptlczoS7hsQHfH2QMH8LoEZk99wKqCNczsnu8bfJllSiMXxzZqYa_ll7i-Wy1myjzdvMArtSggbxqsSdbNRmSQgT6KDbWriJD7ucsEDwuKVe-q9cQMEMU2tO9aeyDbCMFo-FKXPUPzQ5J8xkQU8nn3tNurKVBB8x_8YJ8s0EKg3g' \ --header 'x-8x8-tenant-id: vccC8ProdChecksUS' \ --header 'x-8x8-transmission-time: 1629804577296' \ --data '{"eventType":"AGENT_JOINED","messageType":"SYSTEM","conversationId":"Aka5NMHU8MtIG7lUQOxI0DTOvM4","agentId":"cmalutan","agentName":"Cosmin,Malutan","timestamp":1629804577002}' ``` ## Signature validation To validate a signature: 1. Obtain the request payload and compute the CRC32 **`checksum`** value for it. Note that **`checksum`** should be presented in decimal format. 2. Extract all of the critical HTTP headers described in [**list of custom HTTP headers**](/actions-events/docs/validating-webhook-events#custom-http-headers) 3. Reconstruct the detached payload of the JWS signature as shown in section [data to be signed](/actions-events/docs/validating-webhook-events#data-to-be-signed) This UTF8 encoded JSON will be your **JWS Payload** 4. Construct JWS Signing Input ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.' || (JWS Payload)) 5. Obtain the public key ID from the JOSE Header 6. Obtain the public key in JWK format from the [public key API](/actions-events/reference/getjwkpublickey-1) 7. Validate the **JWS Signature** against the **JWS Signing Input** using the RS256 algorithm and the obtained key ```php webhook(@RequestBody String body, @RequestHeader Map headers) { final byte[] content = body.getBytes(); final String signature = headers.get("x-8x8-signature"); final String customerId = headers.get("x-8x8-customer-id"); final String tenantId = headers.get("x-8x8-tenant-id"); final String eventId = headers.get("x-8x8-event-id"); final String transmissionTime = headers.get("x-8x8-transmission-time"); final String retry = headers.get("x-8x8-retry"); final CRC32 crc32 = new CRC32(); crc32.update(content); final long checksum = crc32.getValue(); final JsonObject json = new JsonObject(); json.addProperty("checksum", checksum); json.addProperty("cid", customerId); json.addProperty("eid", eventId); json.addProperty("retry", Long.valueOf(retry)); json.addProperty("tid", tenantId); json.addProperty("tt", Long.valueOf(transmissionTime)); final String signaturePayload = json.toString(); final Payload payload = new Payload(signaturePayload); try { final JWSObject jwsObject = JWSObject.parse( signature, payload ); final String keyID = jwsObject.getHeader().getKeyID(); final ResponseEntity entity = restTemplate.getForEntity(KEY_URL, String.class, keyID); final RSAKey publicJWK = RSAKey.parse(entity.getBody()); JWSVerifier verifier = new RSASSAVerifier(publicJWK); if (jwsObject.verify(verifier)) { return ResponseEntity.ok().build(); } else { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } } catch (ParseException | JOSEException e) { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } } } ``` ```php unserialize($header_signature); $payload = json_encode([ "checksum" => intval($checksum), "cid" => $header_customerId, "eid" => $header_eventId, "retry" => intval($header_retry), "tid" => $header_tenantId, "tt" => intval($header_transmissionTime) ]); $keyID= $jws->getSignature(0)->getProtectedHeader()["kid"]; $ch = curl_init(sprintf("https://api.8x8.com/vcc/us/chat/v2/jwk/%s/public", $keyID)); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HEADER, 0); $data = curl_exec($ch); curl_close($ch); $jwk = JWK::createFromJson($data); $isVerified = $jwsVerifier->verifyWithKey($jws, $jwk, 0, $payload); if (!$isVerified) { header("HTTP/1.1 401 Unauthorized"); exit; } ``` ```javascript /* In the following example we use the following npm packages: jshashes: ^1.0.8 fastify: ^4.9.2 jose: ^4.10.4, node-fetch: ^3.2.10 */ import * as Hashes from "jshashes" import fastify from 'fastify'; import { errors, flattenedVerify, importJWK, decodeProtectedHeader } from 'jose'; import fetch from 'node-fetch'; const UNAUTHORIZED = 401; const INTERNAL_SERVER_ERROR = 500; const getPublicKey = async(keyId) => { const keyUrl = `https://api.8x8.com/vcc/us/chat/v2/jwk/${keyId}/public`; const response = await fetch(keyUrl); const data = await response.json(); return importJWK(data, 'RS256'); }; const getJwsParts = (token) => { const parts = token.split('.'); if (parts.length === 3 || parts.length === 5) { const [protectedHeaderBase64, , signatureBase64] = parts; return { signatureBase64, protectedHeaderBase64 }; } throw new Error(`Invalid token ${token}`); }; const app = Fastify(); app.post('/callback', async(request, reply) => { const { headers, body } = request; const checksum = Hashes.CRC32(JSON.stringify(body)); const signature = headers['x-8x8-signature']; const customerId = headers['x-8x8-customer-id']; const tenantId = headers['x-8x8-tenant-id']; const eventId = headers['x-8x8-event-id']; const transmissionTime = headers['x-8x8-transmission-time']; const retry = headers['x-8x8-retry']; const signaturePayload = { checksum, cid: customerId, eid: eventId, retry: parseInt(retry, 10), tid: tenantId, tt: parseInt(transmissionTime, 10), }; try { const { signatureBase64, protectedHeaderBase64 } = getJwsParts(signature); const protectedHeader = decodeProtectedHeader(signature); const publicKey = await getPublicKey(protectedHeader.kid); const jws = { signature: signatureBase64, payload: JSON.stringify(signaturePayload), protected: protectedHeaderBase64, }; await flattenedVerify(jws, publicKey); reply.status(200).send(); } catch (err) { if (err.code === errors.JWSSignatureVerificationFailed.code) { reply.status(UNAUTHORIZED).send(err); return; } reply.status(INTERNAL_SERVER_ERROR).send(); } }); // Run the server! try { await app.listen({ port: 3000 }) console.log('Server start http://localhost:3000') } catch (err) { app.log.error(err) process.exit(1) } ``` --- ## Introduction and Use Cases The webchat API, also known as Embedded Chat API, allows configuration via the webchat script that meet some of the following use cases. All methods are available on the `chatApp` instance and return `this`, so they can be chained. ```javascript function fn(chatApp) { chatApp .setCustomerInfo({ "Name": "James" }) .setCustomerLanguage("fr") .setVariables({ "_VIP": "YES" }) .setProxy({ onAppEnd: function () { console.log('Chat ended'); } }); } ``` ## Use cases ### Pass customer data into the webchat script from a website A customer may already be logged into the website, therefore you want to pass across the customer details, as well as details that they are already authenticated into the webchat, so this information can be given to the agent when they accept an interaction, or even passed into the ICA chatbot. ### Trigger the invitation based on an event When a customer is on a set page on your website, you want to go straight into the webchat invitation to help engage them with a product on that page. ### Custom Branding: Allow extra configuration on the web widget to allow you to meet brand guidelines A business wants all of the colors on the webchat, including the send, confirm and cancel button to meet their brand guidelines, so using this configuration will help meet that. ### End a chat session programmatically Your application needs to close the webchat when a customer logs out, navigates away, or based on other business logic, without requiring the customer to click the close button inside the widget. ### React to chat lifecycle events You want to track when the chat widget opens, when a session is created, or when the chat ends, so you can update your page UI, trigger analytics events, or coordinate with other parts of your application. ## Available commands | Method | Parameters | Description | |---|---|---| | [`startChat()`](trigger-webchat) | None | Triggers the chat window, simulating a button click. | | [`endChat()`](end-chat) | None | Programmatically ends the active chat session. | | [`setCustomerInfo(info)`](customer-information) | `{ key: value }` | Sets customer information to be forwarded to the agent. Values must be primitives (string, number, boolean). | | [`resetCustomerInfo()`](reset-customer-information) | None | Clears all previously set customer information. | | [`setCustomerLanguage(lang)`](chat-language) | Language code (e.g. `"fr"`, `"es"`) | Sets the customer language for real-time translation. | | [`setVariables(vars)`](set-chat-variables) | `{ key: value }` | Sets CRM and custom script variables. Keys must be prefixed with `$` (CRM) or `_` (custom). | | [`setInvitationQuickReplies(replies)`](invitation-quick-replies) | `["reply1", "reply2"]` | Sets quick reply suggestions shown in the invitation UI. | | [`setNotificationSoundToggle(enabled)`](notification-sound) | `true` or `false` | Enables or disables notification sounds for incoming messages. | | [`setProxy(callbacks)`](event-callbacks) | `{ onAppEnd: fn, ... }` | Registers lifecycle event callbacks. See [Event callbacks](event-callbacks) for the full list. | ## Getting started Before using the API, you need to set up the [webchat script](webchat-script) on your website. Once the script is loaded, the `chatApp` instance is passed to your callback function, where you can call any of the methods above. ### Prerequisite Before using this document, you should refer to [here](https://docs.8x8.com/8x8WebHelp/VCC/configuration-manager-general/content/enhancedchatscript.htm) to configure your webchat widget. --- ## Webchat script Once you have created the webchat script, when you select the code, it will look like this, which is what will be added to the website for it to work ```html ``` :::note If your website enforces a Content Security Policy, see [Content Security Policy](./content-security-policy.md) for the directives the widget requires — a blank or unstyled widget is the usual symptom of a policy that blocks it. ::: --- ## Webhooks Before you can set up your Chat channel, you must first create a Webhook. Whenever an agent adds a message to a conversation that you create using the Chat API, 8x8 makes an HTTP call to the Webhook URL. A Webhook can be created either via UI or via API. ## Create a Webhook using Configuration Manager 1. Access your **[Configuration Manager](https://docs.8x8.com/8x8WebHelp/VCC/configuration-manager-general/content/cfgoverview.htm)** implementation. 2. Go to **Integrations > Webhooks**. ![3322](../images/fbd3cf3-Screenshot_2021-07-06_at_16.03.36.png "Screenshot 2021-07-06 at 16.03.36.png") 3. Click **Add webhook** ![add webhook](../images/495e6f2-add-webhook.png) 3. Click **Save** ## Create a Webhook using API 1. Access your **[API key](/actions-events/docs/api-key)** 2. Call the **[Create a WebHook endpoint](/actions-events/reference/createwebhook-1)** --- ## Webhooks Events Reference Webhook events are how 8x8 notifies bots or integrations when something happens in a conversation, such as when an agent sends a message, a member joins, or a conversation is transferred. Events are sent as **`POST`** requests to your webhook endpoint. ## Common event envelope All webhook events share a common envelope with the following base fields: | Field | Type | Description | | --- | --- | --- | | `eventType` | string | The type of event. Determines the structure of the rest of the payload. | | `conversationId` | string | The unique identifier of the conversation this event belongs to. | | `timestamp` | number | Unix timestamp (in milliseconds) of when the event occurred. | ```json { "eventType": "", "conversationId": "ID-0", "timestamp": 1713100000000 } ``` ## Events overview The following table lists the top-level event types that can be sent to your webhook: | Event | Description | | --- | --- | | [**`CONVERSATION_UPDATE`**](#conversation_update) | The conversation state or assignment has changed. | | [**`QUEUED`**](#queued) | The interaction has been queued for processing. | | [**`MEMBERS_CHANGED`**](#members_changed) | A participant (agent or user) joined or left the conversation. | | [**`TRANSFER`**](#transfer) | The conversation was transferred to another queue. | | [**`MESSAGE`**](#message) | A new message was added to the conversation. | | [**`ACTIVITY`**](#activity) | A non-message action occurred (typing, adaptive card submission, quick reply, etc.). | | [**`WEB_HOOK_VERIFY`**](#web_hook_verify) | A verification request to confirm the webhook endpoint is reachable. | :::note The **`ACTIVITY`** event type has multiple subtypes differentiated by the `data.name` field. See [Activity events](#activity) for details. ::: --- ## Lifecycle events ### CONVERSATION_UPDATE | Property | Value | | --- | --- | | `eventType` | `CONVERSATION_UPDATE` | | **Channels** | All | The **`CONVERSATION_UPDATE`** event is sent whenever the conversation state, assignment, or user information changes. This includes when a conversation is created, becomes active, or goes idle. #### Data fields | Field | Type | Description | | --- | --- | --- | | `data.state` | string | The current conversation state. One of: `"created"`, `"active"`, `"idle"`. | | `data.assignment` | object | Information about the current assignment. | | `data.assignment.id` | string | The identifier of the assigned resource (agent ID, queue ID, or script ID). | | `data.assignment.type` | string | The assignment type. One of: `"agent"`, `"queue"`, `"script"`. | | `data.assignment.resourceId` | string | The resource identifier. Optional; included when available. | | `data.user` | object | Information about the customer/user in the conversation. | | `data.user.name` | string | The user's display name. | | `data.user.userId` | string | The user's unique identifier. | | `data.user.email` | string | The user's email address. | | `data.user.phone` | string | The user's phone number. | | `data.user.company` | string | The user's company name. | | `data.user.caseId` | string | A case identifier associated with the conversation. | | `data.user.language` | string | The user's language code (e.g., `"en"`). | | `data.user.ipaddress` | string | The user's IP address (when available). | | `data.user.timezoneOffset` | number | The user's timezone offset from UTC (when available). | | `data.user.additionalProperties` | array | A list of custom key-value pairs. Each item has `key`, `value`, and optionally `id`. | | `data.msisdn` | string | The user's phone number in MSISDN format (for telephony-based channels). | | `data.channel` | string | The channel display type (e.g., `"WhatsApp"`, `"SMS"`, `"WebChat"`, `"Facebook"`, `"Viber"`, `"RCS"`, `"Email"`, `"ChatAPI"`). | #### Example ```json { "eventType": "CONVERSATION_UPDATE", "conversationId": "ID-0", "timestamp": 1713100000000, "data": { "state": "active", "assignment": { "type": "agent", "id": "agent-abc-123", "resourceId": "resource-123" }, "user": { "name": "John Doe", "userId": "user-456", "email": "john@example.com", "phone": "+1234567890", "company": "Acme Inc.", "caseId": "CASE-789", "language": "en", "ipaddress": "192.168.1.1", "timezoneOffset": -300, "additionalProperties": [ { "key": "department", "value": "support" } ] }, "msisdn": "+1234567890", "channel": "WhatsApp" } } ``` ### QUEUED | Property | Value | | --- | --- | | `eventType` | `QUEUED` | | **Channels** | All | The **`QUEUED`** event is sent whenever the interaction is being queued for processing. The `queueId` and `queueName` attributes provide information about the queue in which your conversation is waiting. You can use the queue ID to fetch additional information (e.g., statistical data) about the queue like the **average waiting time in queue** or **queue size**. #### Data fields | Field | Type | Description | | --- | --- | --- | | `data.queueId` | string | The unique identifier of the queue. | | `data.queueName` | string | The display name of the queue. | #### Example ```json { "eventType": "QUEUED", "conversationId": "ID-0", "timestamp": 1713100000000, "data": { "queueId": "queue-abc-123", "queueName": "Customer Support" } } ``` ### MEMBERS_CHANGED | Property | Value | | --- | --- | | `eventType` | `MEMBERS_CHANGED` | | **Channels** | All | The **`MEMBERS_CHANGED`** event is sent whenever members other than the bot joined or left the conversation. An agent **joined** event indicates that the agent has seen the messages that were added to the conversation prior to the integration, and will observe any follow-up messages. An agent **left** event indicates the agent has left the conversation. #### Data fields | Field | Type | Description | | --- | --- | --- | | `data.memberType` | string | The type of member. One of: `"user"`, `"agent"`. | | `data.change` | string | The type of change. One of: `"joined"`, `"left"`. | | `data.id` | string | The identifier of the member who joined or left. For agents, this is the agent ID. | #### Example ```json { "eventType": "MEMBERS_CHANGED", "conversationId": "ID-0", "timestamp": 1713100000000, "data": { "memberType": "agent", "change": "joined", "id": "agent-abc-123" } } ``` ### TRANSFER | Property | Value | | --- | --- | | `eventType` | `TRANSFER` | | **Channels** | All | The **`TRANSFER`** event is sent whenever the handling agent transfers the conversation to another queue. This indicates that a [**`MEMBERS_CHANGED`**](#members_changed) event for a `memberType` of `"agent"` with a change of `"left"` does **not** mark the end of the conversation. Another [**`MEMBERS_CHANGED`**](#members_changed) event will follow with a change of `"joined"` for a new agent. This event does not include a `data` field. #### Example ```json { "eventType": "TRANSFER", "conversationId": "ID-0", "timestamp": 1713100000000 } ``` --- ## Message events ### MESSAGE | Property | Value | | --- | --- | | `eventType` | `MESSAGE` | | **Channels** | All | The **`MESSAGE`** event is sent whenever a participant adds a new message to the conversation. Messages can contain text, attachments, rich cards, or a combination of these. #### Data fields | Field | Type | Description | | --- | --- | --- | | `data.isEcho` | boolean | `true` if this message was sent by the bot itself (echoed back). | | `data.sender` | object | Information about who sent the message. | | `data.sender.id` | string | The sender's identifier. | | `data.sender.type` | string | The sender's type (e.g., `"agent"`, `"user"`). | | `data.text` | string | The text content of the message. | | `data.attachments` | array | A list of file attachments included with the message. | | `data.attachments[].id` | string | The attachment identifier. | | `data.attachments[].type` | string | The attachment type (e.g., `"image"`, `"video"`, `"audio"`, `"file"`). | | `data.attachments[].preSignedUrl` | string | A pre-signed URL to download the attachment. | | `data.attachments[].fileName` | string | The original file name of the attachment. | | `data.cards` | array | A list of rich cards (e.g., Adaptive Cards). | | `data.cards[].contentType` | string | The card format (e.g., `"application/vnd.microsoft.card.adaptive"`). | | `data.cards[].content` | object | The card content payload. | #### Example ```json { "eventType": "MESSAGE", "conversationId": "ID-0", "timestamp": 1713100000000, "data": { "isEcho": false, "sender": { "id": "agent-abc-123", "type": "agent" }, "text": "Hello, how can I help you?", "attachments": [ { "id": "att-001", "type": "image", "preSignedUrl": "https://storage.example.com/attachments/att-001?signature=abc", "fileName": "screenshot.png" } ], "cards": [ { "contentType": "application/vnd.microsoft.card.adaptive", "content": {} } ] } } ``` --- ## Activity events {#activity} Activity events share the `eventType` value `"ACTIVITY"` but are differentiated by the `data.name` field. Each activity subtype represents a different kind of non-message action. The common activity envelope looks like this: ```json { "eventType": "ACTIVITY", "conversationId": "ID-0", "timestamp": 1713100000000, "data": { "name": "", "value": {} } } ``` ### Typing indicator | Property | Value | | --- | --- | | `data.name` | `typing` | | **Channels** | WebChat, WhatsApp, RCS, ChatAPI | The **typing** activity is sent when participants are actively typing in the conversation. If you receive a typing event with an **empty** `users` list, it means the participants stopped typing. #### Data fields | Field | Type | Description | | --- | --- | --- | | `data.value.users` | array | A list of users who are currently typing. Empty array means typing has stopped. | | `data.value.users[].id` | string | The identifier of the typing user. | | `data.value.users[].type` | string | The type of the typing user (e.g., `"agent"`, `"user"`). | #### Example: Active typing ```json { "eventType": "ACTIVITY", "conversationId": "ID-0", "timestamp": 1713100000000, "data": { "name": "typing", "value": { "users": [ { "type": "agent", "id": "agent-abc-123" } ] } } } ``` #### Example: Typing stopped ```json { "eventType": "ACTIVITY", "conversationId": "ID-0", "timestamp": 1713100000000, "data": { "name": "typing", "value": { "users": [] } } } ``` ### Adaptive Card action | Property | Value | | --- | --- | | `data.name` | `adaptiveCard/action` | | **Channels** | WebChat | The **Adaptive Card action** activity is sent when a user interacts with an [Adaptive Card (v1.3)](https://adaptivecards.io/). The `type` indicates the kind of action that was triggered, and the `data` object contains the submitted values. #### Data fields | Field | Type | Description | | --- | --- | --- | | `data.value.type` | string | The action type. One of: `"Action.Submit"`, `"Action.Execute"`. | | `data.value.data` | object | The data payload submitted by the user through the Adaptive Card. | #### Example ```json { "eventType": "ACTIVITY", "conversationId": "ID-0", "timestamp": 1713100000000, "data": { "name": "adaptiveCard/action", "value": { "type": "Action.Submit", "data": { "feedbackRating": 5, "comments": "Great service!" } } } } ``` ### Quick Reply action | Property | Value | | --- | --- | | `data.name` | `quickReply/action` | | **Channels** | WebChat | The **Quick Reply action** activity is sent when a user selects a quick reply option. The `title` contains the display text the user selected, and the `payload` contains the programmatic identifier. #### Data fields | Field | Type | Description | | --- | --- | --- | | `data.value.type` | string | The action type. Currently always `"postback"`. | | `data.value.data.title` | string | The display text of the selected quick reply option. | | `data.value.data.payload` | string | The programmatic payload identifier of the selected option. | #### Example ```json { "eventType": "ACTIVITY", "conversationId": "ID-0", "timestamp": 1713100000000, "data": { "name": "quickReply/action", "value": { "type": "postback", "data": { "title": "Check order status", "payload": "order_status" } } } } ``` ### Read receipts | Property | Value | | --- | --- | | `data.name` | `read-receipts` | | **Channels** | None (not yet supported) | :::caution The read receipts event structure is defined in the API but **no channel currently supports sending or displaying read receipts**. This section is provided for reference only. ::: The **read receipts** activity would be sent when participants read messages in the conversation. #### Example ```json { "eventType": "ACTIVITY", "conversationId": "ID-0", "timestamp": 1713100000000, "data": { "name": "read-receipts", "value": [ { "timestamp": 1713100000000, "users": [ { "id": "agent-abc-123", "type": "agent" } ] } ] } } ``` ### CPaaS activity | Property | Value | | --- | --- | | `data.name` | Varies by provider | | **Channels** | WhatsApp, RCS, Viber | The **CPaaS activity** is sent for channel-specific interactive events from CPaaS providers (WhatsApp, RCS, Viber). The `data.name` and `data.value` structure vary depending on the channel and the type of interaction. #### Data fields | Field | Type | Description | | --- | --- | --- | | `data.name` | string | A channel-specific activity type identifier. | | `data.value` | object | The activity payload. Structure varies by channel and activity type. | #### Example ```json { "eventType": "ACTIVITY", "conversationId": "ID-0", "timestamp": 1713100000000, "data": { "name": "interactive/reply", "value": { "type": "button_reply", "button_reply": { "id": "btn-1", "title": "Yes" } } } } ``` --- ## System events ### WEB_HOOK_VERIFY When you add a webhook, you receive a **`WEB_HOOK_VERIFY`** verification event to confirm that a server is listening at the webhook address. This prevents typos, invalid URLs, and other issues that may require further troubleshooting. The server that receives this verification event **must respond with a 2XX status code**. :::note This event includes a `notificationVersion` field and does **not** include `conversationId` or `timestamp` fields. ::: #### Example ```json { "notificationVersion": "Chat Gateway v1.0", "eventType": "WEB_HOOK_VERIFY" } ``` --- ## 8x8 Contact Center Call API Reference import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; The 8x8 Contact Center Call API facilitates contact center management of agent calls. Using the Call API you can designate a specific agent to either respond to or join a call. The Call API manages and assigns phone contacts along with transaction codes and interaction IDs. The Call API enables you to: * Create an agent assigned outbound phone interaction for calling a desired number * Amend the transaction codes for an interaction * End a phone call based on the **`interactionId`** * Hang up the call for an agent (If the call is a conference or call was transferred it will not hang up for the other participants.) * Free up a telephone line for the specified agent or agents in preparation for the next call The username is the tenantId and the password is the VCC action-token. These values are used for the Agent Status API. Security Scheme Type: http HTTP Authorization Scheme: basic Contact VCC Engineering Team: [vcc-dev@8x8.com](mailto:vcc-dev@8x8.com) --- ## 8x8 Contact Center Chat API V2 import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; Integration with the 8x8 Contact Center (CC) Chat API enables you to: * Respond in real time to your customer's chat inquiries * Initiate chat conversations between your customers and agents * Quickly access customer account information in order to better facilitate and enhance your agent's chat session * Allocate your company's resources to best address customer chat requirements * Review conversation information between your customer and the chat message receiving bot * Forward system events (e.g., agent joining conversation, agent ending conversation) to chat seesion stakeholders * Manage customer chats in your agent's supported language(s) or use the automatic translation tool The 2 way API communication is achieved by using callbacks from 8x8's platform to your subscribed URIs. Bearer Token obtained from https://api.8x8.com Security Scheme Type: http HTTP Authorization Scheme: bearer Bearer Token obtained from https://api.8x8.com Security Scheme Type: http HTTP Authorization Scheme: basic Contact [cc-cluj-oncall@8x8.com](mailto:cc-cluj-oncall@8x8.com) --- ## 8x8 Contact Center Dynamic Campaigns import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; The 8x8 Contact Center Dynamic Campaigns API: * Adds and removes records from an active campaign * Sends records to a specified campaign via the API * Adds records to a live campaign * Removes records from a campaign so they are not dialed again * Schedules a callback with a possible maximum of 7 days in advance * Schedules uploads for a maximum of 5 million records ## Campaign form Your campaign with associated tenant is of the form: **`https://{your tenant url}/api/tstats/campaigns/{campaign-id}`** For example: **`https://vcc-eu5.8x8.com/api/tstats/campaigns/101/`** ## Add a customer to a campaign To add a customer to a campaign: **`https://{your tenant url}/api/tstats/campaigns/{campaign-id}/customers`** For example: **`https://vcc-eu5.8x8.com/api/tstats/campaigns/101/customers`** ## Delete a customer from a campaign To delete a customer from a campaign take the following form: **`https://{your tenant url}/api/tstats/campaigns/{campaign-id}/customers/{customer-id}`** For example: **`https://vcc-eu5.8x8.com/api/tstats/campaigns/101/customers/10000111`** The complete list of platform URLs is available on the [8x8 Contact Center - Platform URL Guide.](https://files.mtstatic.com/site_12249/2500/3?Expires=1601933655&Signature=ebyMEramVyRMGdgkVXwJH~2~-ovb7XLq5zV4Q3oWfsRa8l-6ZZq~sK64tHhRPU-GW7W~lYw22~gBmPbCop9l~uy10KC6skH0xvMqrIbBCQGwitAc-83vP8Lu6vBIQxWP5sGqS6JXekSgqcq7stTw-RMLsFDT8fXK4~qyGoMlqNs_&Key-Pair-Id=APKAJ5Y6AV4GI7A555NA) For further configuration information refer to [8x8 Contact Center Dynamic Campaign API Configuration.](https://support.8x8.com/cloud-contact-center/virtual-contact-center/developers/8x8-contact-center-dynamic-campaign-api-configuration) The username is the tenantId and the password is the CC action-token. Security Scheme Type: http HTTP Authorization Scheme: basic Contact Dynamic Campaigns Team: [ro-rec@8x8.com](mailto:ro-rec@8x8.com) Terms of Service {'http://www.8x8.com/terms-and-conditions'} --- ## Add records to campaign import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Add one or more records (1-100) to a dynamic campaign. Records are validated, CRM data is queried, and phone numbers are checked against DNC lists. Operation is atomic - all records succeed or all fail. --- ## Ammends the transaction codes for both an interaction and the associated agent. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Ammends the transaction codes for both an interaction and the associated agent. --- ## Add an attachment. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Add an attachment that you can later use as a reference when adding a message to the conversation. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Add a new customer to an existing campaign import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Add a new customer to an existing campaign **Note: the campaign must be have dynamic campaign enabled to leverage this feature** --- ## The change status for an existing campaign. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; The change status for an existing campaign. **Note: the campaign must be have dynamic campaign enabled to leverage this feature** --- ## Chapi - Chat API import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; 8x8 Work Chat API Security Scheme Type: apiKey Header parameter name: x-api-key --- ## Contact Center Agent Status API import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; The 8x8 Contact Center Get/Set Agent Status API enables you to set an agent's status or availability in your contact center. You can set the status for a specific contact center agent or perform a bulk operation for setting the status for multiple agents. (e.g., ON_BREAK, WORK_OFFLINE). The available agent status codes and types include the following - * 0 UNKNOWN * 1 LOGGED_OUT * 2 LOGGED_IN * 3 ON_BREAK * 4 WAIT_TRANSACT * 5 WORK_OFFLINE * 6 TRANSACT_OFFERED * 7 PROCESS_TRANSACT * 8 POST_PROCESS * 9 BUSY * 10 DIRECT_CALL * 11 ON_EMAIL The Get/Set Agent Status API enables you to: * Obtain the query status for an agent or agents * Set the status for an agent or multiple agents The data obtained from the Get/Set Agent Status API can be filtered by agent group. Note that pagination is currently not supported. This API is not enabled by default. If you do not have access contact your 8x8 representative. The username is the tenantId and the password is the VCC action-token. These values are used for the Agent Status API. Security Scheme Type: http HTTP Authorization Scheme: basic Contact VCC Engineering Team: [vcc-dev@8x8.com](mailto:vcc-dev@8x8.com) --- ## Contact Center Campaigns API import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; Use the Campaign Management API to control the lifecycle of outbound campaigns and dynamically inject records for dialling from your own systems. ## Prerequisites The campaign must be configured as a **Dynamic Campaign** in Configuration Manager before records can be added via the API. However, campaign state can be changed via this API for both regular and dynamic campaigns. ## Authentication All requests require two headers obtained from Admin Console and Configuration Manager: | Header | Description | |--------|-------------| | `x-api-key` | The **Key** value from your API app in Admin Console (`admin.8x8.com/api-access`) | | `X-8x8-Tenant` | The **Tenant Name** from **Home > Profile** in Configuration Manager | ## Content Type All requests must include the header `Content-Type: application/vnd.campaigns.v1+json`. ## Customer Site The `{customer-site}` path parameter is your site identifier Admin Console API Key with the **Contact Center Campaigns** API Product attached to the app. The key value is the **Key** field from your app at [admin.8x8.com/api-access](https://admin.8x8.com/api-access) and always starts with `eght_`. Security Scheme Type: apiKey Header parameter name: x-api-key Contact 8x8 Support: URL: [https://support.8x8.com](https://support.8x8.com) Terms of Service {'https://www.8x8.com/terms-and-conditions'} --- ## Contact Center Chat Gateway import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; Integration with the 8x8 Contact Center (CC) Chat API Security Scheme Type: apiKey Header parameter name: x-api-key Contact [cc-cluj-iris@8x8.com](mailto:cc-cluj-iris@8x8.com) --- ## Creates a new access token that can be used for API access. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Creates a new access token that can be used for API access. --- ## Creates a new conversation. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Creates a new transaction in the Contact Center and returns a conversation Id as an identifier that can be used later on to send messages to agent and also close the ongoing transaction. English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"},"additionalProperties":{"type":"array","maxProperties":30,"items":{"allOf":[{"type":"object","properties":{"key":{"type":"string","format":"string","example":"customKey"},"value":{"type":"string","format":"string","example":"customValue"}},"example":{"key":"senderName","value":"John Doe"}}],"title":"AdditionalPropertiesPayload"}}}}},"title":"ConversationUserData"},{"type":"object","properties":{"assignment":{"type":"object","properties":{"type":{"type":"string","format":"string","x-extensible-enum":["queue","script"],"default":"queue"},"id":{"type":"string","format":"string","description":"Queue or Script identifier, depending of which type is being used.","example":"100"}}}},"title":"AssignmentField"},{"type":"object","required":["channelId","user"],"properties":{"history":{"type":"object","required":["messages"],"properties":{"messages":{"type":"array","maxLength":200,"items":{"anyOf":[{"allOf":[{"type":"object","required":["authorType"],"properties":{"authorType":{"type":"string","x-extensible-enum":["bot","user"],"default":"user","title":"ExternalAuthorType"},"text":{"type":"string","format":"string","example":"Hello!","description":"Text content for direct messages","required":["text"],"additionalProperties":false}}},{"type":"object","properties":{"attachments":{"description":"Id of an attachment uploaded using attachments path.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"}}}}},"title":"Attachments"},{"type":"object","properties":{"cards":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"string","description":"Adaptive card identifier."},"contentType":{"type":"string","enum":["application/vnd.microsoft.card.adaptive","application/8x8.quick.replies","Interactive","Location","Template","Suggestions","Buttons","Carousel"],"default":"application/vnd.microsoft.card.adaptive","description":"Card content type.","title":"ContentType"},"content":{"type":"object","description":" Any adaptive cards, up to version 1.6 https://adaptivecards.io/schemas/1.6.0/adaptive-card.json\nhttps://adaptivecards.io/explorer/ Any 8x8 Quick Replies card. Any interactive card. Any location card. Any template card. "},"actionSubmitted":{"type":"boolean","description":"Indicates if the card was submitted by the user."},"actionExecuted":{"type":"boolean","description":"Indicates if the card was executed by the agent."}},"title":"AdaptiveCards"}]}}},"title":"Cards"}],"title":"SendConversationMessagePayload"},{"allOf":[{"type":"object","required":["authorType"],"properties":{"authorType":{"type":"string","x-extensible-enum":["bot","user"],"default":"user","title":"ExternalAuthorType"}},"allOf":[{"properties":{"externalProvider":{"type":"object","required":["name","externalMessageId"],"properties":{"name":{"type":"string","enum":["X"],"description":"External provider name. Currently only 'X' (X/Twitter) is supported.","example":"X"},"externalMessageId":{"type":"string","description":"External message ID from the provider (e.g., tweet ID for X/Twitter)","example":"1234567890123456789"}},"description":"External provider information for message rehydration. Required when text is not provided. Both text and externalProvider cannot be provided at the same time.","title":"ExternalProviderPayload"}},"required":["externalProvider"],"additionalProperties":false}]},{"type":"object","properties":{"attachments":{"description":"Id of an attachment uploaded using attachments path.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"}}}}},"title":"Attachments"},{"type":"object","properties":{"cards":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"string","description":"Adaptive card identifier."},"contentType":{"type":"string","enum":["application/vnd.microsoft.card.adaptive","application/8x8.quick.replies","Interactive","Location","Template","Suggestions","Buttons","Carousel"],"default":"application/vnd.microsoft.card.adaptive","description":"Card content type.","title":"ContentType"},"content":{"type":"object","description":" Any adaptive cards, up to version 1.6 https://adaptivecards.io/schemas/1.6.0/adaptive-card.json\nhttps://adaptivecards.io/explorer/ Any 8x8 Quick Replies card. Any interactive card. Any location card. Any template card. "},"actionSubmitted":{"type":"boolean","description":"Indicates if the card was submitted by the user."},"actionExecuted":{"type":"boolean","description":"Indicates if the card was executed by the agent."}},"title":"AdaptiveCards"}]}}},"title":"Cards"}],"title":"SendConversationMessageWithExternalProviderPayload"}]}}}}}}],"title":"CreateTransactionRequest"}}}}} > --- ## Creates a new conversation.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Creates a new transaction in the Contact Center and returns a conversation Id as an identifier that can be used later on to send messages to agent and also close the ongoing transaction. English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"}},"additionalProperties":{"type":"string"},"description":"The attached data which contains customer details. The data fields include `senderName`, `customerId`, `email`, `company`, `caseId` and `language` are predefined; however, you can also add any metadata. Any information about the customer can be attached to a Chat API conversation, and it will be shown to Contact Center agent as part of transaction details in the chat panel.\nFor example: { `senderName: John Doe email: john.doe@email.com language: en myCustomProperty: customValue anyKey: anyValue` }","maxProperties":30}},"title":"ConversationCustomerData"},{"type":"object","required":["channelId","user"],"properties":{"history":{"type":"object","required":["messages"],"properties":{"messages":{"type":"array","maxLength":200,"items":{"type":"object","required":["text"],"properties":{"text":{"type":"string","format":"string","example":"Hello world!","description":"Message text content."},"type":{"type":"string","x-extensible-enum":["bot","user"],"default":"user","title":"ExternalAuthorType"}},"title":"HistoryMessageV2"}}}}}}],"title":"CreateTransactionRequest"}}}}} > >: May not be empty."}}},"title":"ConstraintViolationProblem"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"fault":{"type":"object","description":"Error message.","properties":{"faultstring":{"type":"string","example":"Invalid Access Token"},"detail":{"type":"object","properties":{"errorcode":{"type":"string","example":"keymanagement.service.invalid_access_token"}}}}}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Creates a channel. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Create a new channel for a customer. --- ## Creates a channel.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Create a new channel for a customer. > does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Creates a post agent assignment for a conversation. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Creates a post-agent assignment for a conversation. This endpoint allows the conversation to be handed back to the bot after an agent finishes their interaction, to ensure seamless continuation of the customer's digital journey after agent interactions, allowing for tasks such as surveys or further automated assistance. The flexible assignment mechanism supports various post-conversation workflows and is designed to be extensible for different use cases after the agent engagement has concluded. --- ## Creates a new customer webhook. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Creates a new customer webhook. --- ## Creates a new customer webhook.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Creates a new customer webhook. >: May not be empty."}}},"title":"ConstraintViolationProblem"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"fault":{"type":"object","description":"Error message.","properties":{"faultstring":{"type":"string","example":"Invalid Access Token"},"detail":{"type":"object","properties":{"errorcode":{"type":"string","example":"keymanagement.service.invalid_access_token"}}}}}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Customers leaves conversation. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; When customers leave a conversation. It translates to a current interaction that is being terminated by the customer. > does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Confirms that the customer left the interaction."}}} > --- ## Ends a phone call based on the interactionId. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Ends a phone call based on the interactionId. --- ## Delete ChatAPI channel by Id. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Deletes ChatAPI channel by the provided Id. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Delete ChatAPI channel by Id.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Deletes ChatAPI channel by the provided Id. > does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## This method deletes customers from an existing campaign. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method deletes a customer from an existing campaign. You can delete new, queued, or scheduled customers. You should not delete customers that have received, completed, or already deleted offers. If the customer has been offered the delete method it will fail and if the agent rejects the call it be re-queued again. The delete method can then be called again. **Note: the campaign must be have dynamic campaign enabled to leverage this feature** --- ## Deletes the webhook by Id. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Deletes the webhook by Id. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Deletes the webhook by Id.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Deletes the webhook by Id. > does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Download attachment. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Download attachment contents. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Obtain the status for tenant agents. Agents can be filtered by group. Pagination is currently not available. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Obtain the status for tenant agents. Agents can be filtered by group. Pagination is currently not available. --- ## Obtains the status for a specific agent. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Obtains the status for a specific agent. --- ## Retrieves the list of conversation message cards. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves list of conversation message cards. Either Adaptive Cards or Quick Replies. --- ## Returns all conversations belonging to the customer. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns a list of all conversations belonging to the customer that comply with the provided filters. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Retrieves conversation details. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves conversation details. English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"},"additionalProperties":{"type":"array","maxProperties":30,"items":{"allOf":[{"type":"object","properties":{"key":{"type":"string","format":"string","example":"customKey"},"value":{"type":"string","format":"string","example":"customValue"}},"example":{"key":"senderName","value":"John Doe"}}],"title":"AdditionalPropertiesPayload"}}}}},"title":"ConversationUserData"},{"type":"object","properties":{"id":{"type":"string","example":"vXg39aMTRlq4xCBFaUCTlA","description":"Conversation / Transaction identifier."},"state":{"type":"string","enum":["created","active","idle"],"example":"idle","description":"Conversation state."}}}],"title":"ConversationResult"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"errors":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"code":{"type":"string","example":"access_denied"}}}},"referenceId":{"type":"string","example":"dacd0dd252723a2"}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Header X-8x8-Tenant doesn't match tenant information for customerId=aaaa"}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Retrieves conversation details.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves conversation details. English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"}},"additionalProperties":{"type":"string"},"description":"The attached data which contains customer details. The data fields include `senderName`, `customerId`, `email`, `company`, `caseId` and `language` are predefined; however, you can also add any metadata. Any information about the customer can be attached to a Chat API conversation, and it will be shown to Contact Center agent as part of transaction details in the chat panel.\nFor example: { `senderName: John Doe email: john.doe@email.com language: en myCustomProperty: customValue anyKey: anyValue` }","maxProperties":30}},"title":"ConversationCustomerData"},{"type":"object","properties":{"id":{"type":"string","example":"vXg39aMTRlq4xCBFaUCTlA","description":"Conversation / Transaction identifier."},"startTime":{"type":"string","format":"date-time"},"interactions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"string","example":"int-170392a3b7d-5qyRMnJL6nw63SLHrfQirLO3C-chat-00-tenant01"},"startTime":{"type":"string","format":"date","example":"2020-08-05T11:55:00.592Z"},"endTime":{"type":"string","format":"date","example":"2020-08-05T11:55:00.592Z"}},"title":"InteractionInfo"}}}}],"title":"ConversationResult"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"fault":{"type":"object","description":"Error message.","properties":{"faultstring":{"type":"string","example":"Invalid Access Token"},"detail":{"type":"object","properties":{"errorcode":{"type":"string","example":"keymanagement.service.invalid_access_token"}}}}}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Returns all conversations belonging to the customer.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns a list of all conversations belonging to the customer that comply with the provided filters. English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"},"additionalProperties":{"type":"array","maxProperties":30,"items":{"allOf":[{"type":"object","properties":{"key":{"type":"string","format":"string","example":"customKey"},"value":{"type":"string","format":"string","example":"customValue"}},"example":{"key":"senderName","value":"John Doe"}}],"title":"AdditionalPropertiesPayload"}}}}},"title":"ConversationUserData"},{"type":"object","properties":{"id":{"type":"string","example":"vXg39aMTRlq4xCBFaUCTlA","description":"Conversation / Transaction identifier."},"state":{"type":"string","enum":["created","active","idle"],"example":"idle","description":"Conversation state."}}}],"title":"ConversationResult"}}}}}},{"type":"object","properties":{"_links":{"type":"object","properties":{"self":{"type":"object","properties":{"href":{"type":"string"}}}}}},"title":"Links"},{"type":"object","properties":{"page":{"type":"object","properties":{"size":{"type":"number","format":"int32","description":"Page size of the response."},"number":{"type":"number","format":"int32","description":"Page 0-based index of the response."},"totalElements":{"type":"number","format":"int32","description":"Total number of items."},"totalPages":{"type":"number","format":"int32","description":"Total number of pages."}}}},"title":"PageResult"}],"title":"ConversationListResult"}}}},"400":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"errors":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"code":{"type":"string","example":"access_denied"}}}},"referenceId":{"type":"string","example":"dacd0dd252723a2"}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Header X-8x8-Tenant doesn't match tenant information for customerId=aaaa"}}},"title":"ForbiddenViolationProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Returns all conversations belonging to the customer.(3) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns a list of all conversations belonging to the customer that comply with the provided filters. ","in":"query","required":false,"schema":{"type":"string","format":"string","default":"customerKeyValue"}}]} > English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"}},"additionalProperties":{"type":"string"},"description":"The attached data which contains customer details. The data fields include `senderName`, `customerId`, `email`, `company`, `caseId` and `language` are predefined; however, you can also add any metadata. Any information about the customer can be attached to a Chat API conversation, and it will be shown to Contact Center agent as part of transaction details in the chat panel.\nFor example: { `senderName: John Doe email: john.doe@email.com language: en myCustomProperty: customValue anyKey: anyValue` }","maxProperties":30}},"title":"ConversationCustomerData"},{"type":"object","properties":{"id":{"type":"string","example":"vXg39aMTRlq4xCBFaUCTlA","description":"Conversation / Transaction identifier."},"startTime":{"type":"string","format":"date-time"},"interactions":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","format":"string","example":"int-170392a3b7d-5qyRMnJL6nw63SLHrfQirLO3C-chat-00-tenant01"},"startTime":{"type":"string","format":"date","example":"2020-08-05T11:55:00.592Z"},"endTime":{"type":"string","format":"date","example":"2020-08-05T11:55:00.592Z"}},"title":"InteractionInfo"}}}}],"title":"ConversationResult"}}}},{"type":"object","properties":{"page":{"type":"object","properties":{"pageSize":{"type":"number","format":"int32","description":"Page size of the response."},"pageIndex":{"type":"number","format":"int32","description":"Page 0-based index of the response."},"elementCount":{"type":"number","format":"int32","description":"Total number of items."},"pageCount":{"type":"number","format":"int32","description":"Total number of pages."}}}},"title":"PageResult"}],"title":"ConversationListResult"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"fault":{"type":"object","description":"Error message.","properties":{"faultstring":{"type":"string","example":"Invalid Access Token"},"detail":{"type":"object","properties":{"errorcode":{"type":"string","example":"keymanagement.service.invalid_access_token"}}}}}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Get ChatAPI channel by Id. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves ChatAPI channel by Id. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Get ChatAPI channel by Id.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves ChatAPI channel by Id. > does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Retrieves all customer Chat API channels. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves all customer Chat API channels based on the provided access token information. --- ## Retrieves all customer Chat API channels.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves all customer Chat API channels based on the provided access token information. > does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Get an attachment from Conversation. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Get an attachment from conversation along with their details. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Get attachments in Conversation. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Get all attachments added to the conversation along with their details. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Returns the public JWK. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns the public JWK that can be used to validate the message signature. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Returns the public JWK.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns the public JWK that can be used to validate the message signature. > does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"type":{"type":"string","format":"string","description":"A string that identifies the Error code class.","example":"ClientError"},"title":{"type":"string","description":"A short, summary of the problem type. Written in english and readable\nfor engineers (usually not suited for non technical stakeholders and\nnot localized); \n","example":"Resource not found."},"status":{"type":"integer","format":"int32","description":"The HTTP status code generated by the origin server for this problem occurrence.\n","minimum":100,"maximum":600,"exclusiveMaximum":true,"example":404},"detail":{"type":"string","description":"Could not locate the resource. The resource may not exist or is not accessible.\n","example":"Resource may not exist or is not accessible."},"resourceType":{"type":"string","format":"string","example":"JWKPublic"},"resourceId":{"type":"string","format":"string","example":"EXaiWfZe20eSjklZ70074w"},"instance":{"type":"string","format":"string","description":"The specific error code that points to the problem. It is a subclass of the Type error code class.\n","example":"ResourceNotFound"}}}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Retrieves the conversation messages. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the conversation messages that took place during the lifespan of the conversation. English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"}},"title":"ConversationParticipant"}]},"externalProvider":{"type":"object","required":["name","externalMessageId"],"properties":{"name":{"type":"string","enum":["X"],"description":"External provider name. Currently only 'X' (X/Twitter) is supported.","example":"X"},"externalMessageId":{"type":"string","description":"External message ID from the provider (e.g., tweet ID for X/Twitter)","example":"1234567890123456789"}},"description":"External provider information for message rehydration. Required when text is not provided. Both text and externalProvider cannot be provided at the same time.","title":"ExternalProviderPayload"}}},{"type":"object","properties":{"attachments":{"description":"Id of an attachment uploaded using attachments path.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"}}}}},"title":"Attachments"},{"type":"object","properties":{"cards":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"string","description":"Adaptive card identifier."},"contentType":{"type":"string","enum":["application/vnd.microsoft.card.adaptive","application/8x8.quick.replies","Interactive","Location","Template","Suggestions","Buttons","Carousel"],"default":"application/vnd.microsoft.card.adaptive","description":"Card content type.","title":"ContentType"},"content":{"type":"object","description":" Any adaptive cards, up to version 1.6 https://adaptivecards.io/schemas/1.6.0/adaptive-card.json\nhttps://adaptivecards.io/explorer/ Any 8x8 Quick Replies card. Any interactive card. Any location card. Any template card. "},"actionSubmitted":{"type":"boolean","description":"Indicates if the card was submitted by the user."},"actionExecuted":{"type":"boolean","description":"Indicates if the card was executed by the agent."}},"title":"AdaptiveCards"}]}}},"title":"Cards"}],"title":"MessageResult"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"errors":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"code":{"type":"string","example":"access_denied"}}}},"referenceId":{"type":"string","example":"dacd0dd252723a2"}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Header X-8x8-Tenant doesn't match tenant information for customerId=aaaa"}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Retrieves the conversation messages.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the conversation messages that took place during the lifespan of the conversation. English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"}},"title":"ConversationParticipant"}]},"externalProvider":{"type":"object","required":["name","externalMessageId"],"properties":{"name":{"type":"string","enum":["X"],"description":"External provider name. Currently only 'X' (X/Twitter) is supported.","example":"X"},"externalMessageId":{"type":"string","description":"External message ID from the provider (e.g., tweet ID for X/Twitter)","example":"1234567890123456789"}},"description":"External provider information for message rehydration. Required when text is not provided. Both text and externalProvider cannot be provided at the same time.","title":"ExternalProviderPayload"}}},{"type":"object","properties":{"attachments":{"description":"Id of an attachment uploaded using attachments path.","type":"array","items":{"type":"object","properties":{"id":{"type":"string"}}}}},"title":"Attachments"},{"type":"object","properties":{"cards":{"type":"array","items":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","format":"string","description":"Adaptive card identifier."},"contentType":{"type":"string","enum":["application/vnd.microsoft.card.adaptive","application/8x8.quick.replies","Interactive","Location","Template","Suggestions","Buttons","Carousel"],"default":"application/vnd.microsoft.card.adaptive","description":"Card content type.","title":"ContentType"},"content":{"type":"object","description":" Any adaptive cards, up to version 1.6 https://adaptivecards.io/schemas/1.6.0/adaptive-card.json\nhttps://adaptivecards.io/explorer/ Any 8x8 Quick Replies card. Any interactive card. Any location card. Any template card. "},"actionSubmitted":{"type":"boolean","description":"Indicates if the card was submitted by the user."},"actionExecuted":{"type":"boolean","description":"Indicates if the card was executed by the agent."}},"title":"AdaptiveCards"}]}}},"title":"Cards"}],"title":"MessageResult"}}}}}},{"type":"object","properties":{"page":{"type":"object","properties":{"size":{"type":"number","format":"int32","description":"Page size of the response."},"number":{"type":"number","format":"int32","description":"Page 0-based index of the response."},"totalElements":{"type":"number","format":"int32","description":"Total number of items."},"totalPages":{"type":"number","format":"int32","description":"Total number of pages."}}}},"title":"PageResult"}],"title":"MessageListResult"}}}},"400":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"errors":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"code":{"type":"string","example":"access_denied"}}}},"referenceId":{"type":"string","example":"dacd0dd252723a2"}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Header X-8x8-Tenant doesn't match tenant information for customerId=aaaa"}}},"title":"ForbiddenViolationProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Retrieves the conversation messages.(3) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the conversation messages that took place during the lifepan of the conversation. > does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Fetches messages for a room. Defaults to `CHAPI sandbox` room. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Fetches a list of messages from the room. --- ## Retrieve the conversation participants. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves all the participants that joined during the lifespan of the conversation. English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"}},"title":"ConversationParticipant"}}}}}},{"type":"object","properties":{"_links":{"type":"object","properties":{"self":{"type":"object","properties":{"href":{"type":"string"}}}}}},"title":"Links"},{"type":"object","properties":{"page":{"type":"object","properties":{"size":{"type":"number","format":"int32","description":"Page size of the response."},"number":{"type":"number","format":"int32","description":"Page 0-based index of the response."},"totalElements":{"type":"number","format":"int32","description":"Total number of items."},"totalPages":{"type":"number","format":"int32","description":"Total number of pages."}}}},"title":"PageResult"}],"title":"ConversationParticipants"}}}},"400":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"errors":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"code":{"type":"string","example":"access_denied"}}}},"referenceId":{"type":"string","example":"dacd0dd252723a2"}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Header X-8x8-Tenant doesn't match tenant information for customerId=aaaa"}}},"title":"ForbiddenViolationProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Retrieve the conversation participants.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves all the participants that joined during the lifespan of the conversation. English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"}},"title":"ConversationParticipants"}}}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"fault":{"type":"object","description":"Error message.","properties":{"faultstring":{"type":"string","example":"Invalid Access Token"},"detail":{"type":"object","properties":{"errorcode":{"type":"string","example":"keymanagement.service.invalid_access_token"}}}}}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Get webhook by Id. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the webhook by the provided Id. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Get webhook by Id.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the webhook by the provided Id. > does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Retrieves all customer webhooks based on the associated token information. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves all customer webhooks based on the associated token information. --- ## Retrieves all customer webhooks based on the associated token information.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves all customer webhooks based on the associated token information. > does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Hangs up the call for an agent. If the call is a conference or call was transferred it will not hang up for the other participants. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Hangs up the call for an agent. If the call is a conference or call was transferred it will not hang up for the other participants. --- ## Frees up a telephone line for the specified agent in preparation for the next call. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Frees up a telephone line for the specified agent in preparation for the next call. --- ## Frees up the agent lines in preparation to take the next call. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Frees up the agent lines in preparation to take the next call. --- ## Modify campaign state import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Change campaign state. State transitions are validated and must follow allowed state machine rules. --- ## Patch a conversation details. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Patch a conversation details. English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"},"additionalProperties":{"type":"array","maxProperties":30,"items":{"allOf":[{"type":"object","properties":{"key":{"type":"string","format":"string","example":"customKey"},"value":{"type":"string","format":"string","example":"customValue"}},"example":{"key":"senderName","value":"John Doe"}}],"title":"AdditionalPropertiesPayload"}}}}},"title":"ConversationUserData"}],"title":"PatchTransactionRequest"}}}}} > English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"},"additionalProperties":{"type":"array","maxProperties":30,"items":{"allOf":[{"type":"object","properties":{"key":{"type":"string","format":"string","example":"customKey"},"value":{"type":"string","format":"string","example":"customValue"}},"example":{"key":"senderName","value":"John Doe"}}],"title":"AdditionalPropertiesPayload"}}}}},"title":"ConversationUserData"},{"type":"object","properties":{"id":{"type":"string","example":"vXg39aMTRlq4xCBFaUCTlA","description":"Conversation / Transaction identifier."},"state":{"type":"string","enum":["created","active","idle"],"example":"idle","description":"Conversation state."}}}],"title":"ConversationResult"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"errors":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"code":{"type":"string","example":"access_denied"}}}},"referenceId":{"type":"string","example":"dacd0dd252723a2"}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Header X-8x8-Tenant doesn't match tenant information for customerId=aaaa"}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Creates an agent assigned outbound phone interaction for calling the desired number. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Creates an agent assigned outbound phone interaction for calling the desired number. --- ## Update a conversation details. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Update a conversation details. English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"},"additionalProperties":{"type":"array","maxProperties":30,"items":{"allOf":[{"type":"object","properties":{"key":{"type":"string","format":"string","example":"customKey"},"value":{"type":"string","format":"string","example":"customValue"}},"example":{"key":"senderName","value":"John Doe"}}],"title":"AdditionalPropertiesPayload"}}}}},"title":"ConversationUserData"},{"type":"object","properties":{"assignment":{"type":"object","properties":{"type":{"type":"string","format":"string","x-extensible-enum":["queue","script"],"default":"queue"},"id":{"type":"string","format":"string","description":"Queue or Script identifier, depending of which type is being used.","example":"100"}}}},"title":"AssignmentField"}],"title":"UpdateTransactionRequest"}}}}} > English\n* `ru` => Русский\n* `de` => Deutsch\n* `ja` => 日本語\n* `es` => Español\n* `fr` => Français\n* `pt` => Português\n* `it` => Italiano\n* `pl` => Polski\n* `hr` => Hrvatski\n* `nl` => Dutch\n* `ar` => العرية\n* `da` => Dansk\n* `ko` => 한국\n* `no` => Norsk\n* `sv` => Svenska\n* `vi` => Tiếng Việt\n* `cy` => Cymraeg\n* `th` => ไทย\n* `zh-CN` => 简体中文\n* `zh-TW` => 中國傳統","title":"Language"},"additionalProperties":{"type":"array","maxProperties":30,"items":{"allOf":[{"type":"object","properties":{"key":{"type":"string","format":"string","example":"customKey"},"value":{"type":"string","format":"string","example":"customValue"}},"example":{"key":"senderName","value":"John Doe"}}],"title":"AdditionalPropertiesPayload"}}}}},"title":"ConversationUserData"},{"type":"object","properties":{"id":{"type":"string","example":"vXg39aMTRlq4xCBFaUCTlA","description":"Conversation / Transaction identifier."},"state":{"type":"string","enum":["created","active","idle"],"example":"idle","description":"Conversation state."}}}],"title":"ConversationResult"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"errors":{"type":"array","items":{"type":"object","properties":{"message":{"type":"string","example":"Access Denied"},"code":{"type":"string","example":"access_denied"}}}},"referenceId":{"type":"string","example":"dacd0dd252723a2"}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Header X-8x8-Tenant doesn't match tenant information for customerId=aaaa"}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Send a message. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Send a message to a conversation represented by the provided ID. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Send a message.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Send a message to a conversation represented by the provided ID. > does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Send a message to a public chat room. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This will send a message to a room. If the room does not exist Chapi will create the room, then send the message to it. This endpoint is unable to send messages if the room is private. --- ## Sends a read receipt for a conversation. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Sends a read receipt to the Chalet room associated with the given conversation, indicating that all messages up to the given timestamp have been read. Requires an authorType of either 'user' or 'bot'. The 'user' authorType is only allowed for CHAT_API channels. --- ## Sends a thinking indicator for a conversation. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Sends a thinking indicator notification to the Chalet backplane room-update resource, signaling that the bot is processing. The request body is optional; when omitted, defaults are applied (authorType='bot', maxThinkingTimeSeconds=60). --- ## Creates a typing indicator for a conversation. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Sends a typing indicator to the Chalet room associated with the given conversation. Requires an authorType of either 'user' or 'bot' to determine the identity used when sending the indicator. --- ## The bulk operation for setting the status for multiple agents. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Setting the status for multiple agents at the same time. --- ## Sets the agent status for a specific agent. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Sets the agent status for a specific agent. --- ## Sends the adaptive card action execute. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Updates the adaptive card action execute flag and sends the activity notification. --- ## Sends the adaptive card action submit. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Updates the adaptive card action submit flag and sends the activity notification. --- ## Update ChatAPI channel by Id. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Update ChatAPI channel by Id. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Update ChatAPI channel by Id.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Update ChatAPI channel by Id. >: May not be empty."}}},"title":"ConstraintViolationProblem"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"fault":{"type":"object","description":"Error message.","properties":{"faultstring":{"type":"string","example":"Invalid Access Token"},"detail":{"type":"object","properties":{"errorcode":{"type":"string","example":"keymanagement.service.invalid_access_token"}}}}}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Updates the full webhook resource by Id. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Updates the full webhook resource by Id. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}}}} > --- ## Updates the full webhook resource by Id.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Updates the full webhook resource by Id. >: May not be empty."}}},"title":"ConstraintViolationProblem"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"fault":{"type":"object","description":"Error message.","properties":{"faultstring":{"type":"string","example":"Invalid Access Token"},"detail":{"type":"object","properties":{"errorcode":{"type":"string","example":"keymanagement.service.invalid_access_token"}}}}}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"404":{"description":"Contains a descriptive information.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Resource of type <> with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## User leaves conversation. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; When user leave a conversation. It translates to a current interaction that is being terminated by the user. > with id <> was not found."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ResourceNotFoundProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"Internal error."},"errors":{"type":"array","items":{"type":"object","properties":{"code":{"type":"string","description":"Unique error code.","example":"invalid_queue"},"message":{"type":"string","description":"Free text error description.","example":"Queue ID 112 is invalid."}}}}},"title":"GenericExceptionPayload"}}}},"default":{"description":"Confirms that the user left the interaction."}}} > --- ## The endpoint used to validate that the webhook is working and reachable. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Validates the webhook URL endpoint from the connectivity point-of-view by sending a hello message and a 2xx success status code. --- ## The endpoint used to validate that the webhook is working and reachable.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Validates the webhook URL endpont from the connectivity point-of-view by sending a hello message and a 2xx success status code. >: May not be empty."}}},"title":"ConstraintViolationProblem"}}}},"401":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"fault":{"type":"object","description":"Error message.","properties":{"faultstring":{"type":"string","example":"Invalid Access Token"},"detail":{"type":"object","properties":{"errorcode":{"type":"string","example":"keymanagement.service.invalid_access_token"}}}}}},"title":"UnauthorizedProblem"}}}},"403":{"description":"Contains a descriptive response.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.","example":"The tenant does not belong to customer."},"errors":{"type":"array","items":{"type":"string","example":"Tenant <> does not belong to customer <>."}}},"title":"ForbiddenViolationProblem"}}}},"500":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}},"default":{"description":"Contains a description of the error.","content":{"application/problem+json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error message.\n","example":"Could not process this request due to ..."},"errors":{"type":"array","description":"List of errors that caused the request to fail.","items":{"type":"string"}},"referenceId":{"type":"string","description":"A referenceId that can be used by 8x8 engineers to track a failed request."}},"title":"InternalServerProblem"}}}}}} > --- ## Administration API Changelog import Changelog from '@site/docusaurus/components/Changelog'; # Administration API Changelog Every change to the Administration API Suite, newest first. Filter by API for a focused view. Change types follow the [API Change Policy](./docs/api-change-policy.md). --- ## API Change Policy **Last Updated:** December 23, 2025 **Applies to:** All Administration API Suite APIs ## Overview At 8x8, we recognize that API stability is critical to your business operations. Changes to APIs, particularly breaking changes, can require significant development effort, testing, and coordination on your part. We take this responsibility seriously and are committed to minimizing disruption to your integrations. **Our commitments to you:** - **Minimize change volume and breaking changes** - We strive to limit the frequency of changes and avoid breaking changes whenever possible by designing APIs with extensibility in mind - **Transparent communication** - We provide clear documentation and advance notice so you can plan accordingly - **Balanced evolution** - While we maintain stability, we continue evolving our APIs to deliver new features and capabilities that add value to your integrations This document outlines how we manage changes to Administration APIs, including our approach to versioning, breaking changes, and how we communicate updates to API consumers. Our goal is to provide you with predictable, well-communicated changes that respect your investment in our platform while enabling continued innovation. ## Change Categories ### Non-Breaking Changes Non-breaking changes enhance existing functionality without disrupting current integrations. These changes include: **Examples:** - **Addition of new attributes** - New fields added to API responses (e.g., adding a `phoneNumberType` field to user objects) - **Addition of enum values** - New possible values for existing enumerated fields (e.g., adding "MOBILE_APP" to existing values "DESKTOP" and "WEB") - **Relaxation of validation rules** - Making requirements less restrictive (e.g., reducing minimum password length from 12 to 8 characters) - **Addition of optional request parameters** - New optional query parameters or request body fields **Important:** Non-breaking changes: - Are applied to the current API version - Do not trigger a version increment - Do not require advance notification - Are documented in the changelog at the time they go live ### Breaking Changes Breaking changes modify existing behavior in ways that may disrupt current integrations. These changes include: **Examples:** - **Removal of attributes** - Deleting fields from API responses - **Removal of enum values** - Removing possible values from enumerated fields - **Renaming of attributes** - Changing field names in requests or responses - **Change in data types** - Modifying the data type of existing fields (e.g., string to integer) - **Stricter validation rules** - Making requirements more restrictive (e.g., increasing minimum password length) - **Removal of endpoints** - Deprecating and removing API operations - **Change in HTTP methods** - Modifying the HTTP verb for existing endpoints - **Change in response status codes** - Altering success or error response codes for existing operations ## API Versioning ### Version Format When breaking changes are necessary, we release a new API version. Versions are specified through a vendor-specific media type, sent in the `Content-Type` header for requests with a payload (`POST`, `PUT`) and in the `Accept` header for requests that return a payload (`GET`). See [API Versioning](./suite-common.mdx#api-versioning) for full details. ```text application/vnd.{resource}.v{major}+json ``` **Example:** ```text application/vnd.users.v1+json ``` ### New Version Release Process When a new API version is published: 1. **Testing availability** - The new version becomes available for testing 2. **Parallel operation** - Both old and new versions remain fully functional 3. **Consumer control** - You control the transition by adjusting the version media type (in the `Content-Type` or `Accept` header, depending on the endpoint) in your requests 4. **No impact to current version** - Existing implementations continue working unchanged ### Version Support and Deprecation - **Current versions** remain supported until formally deprecated - **Deprecation notice** - When a version is deprecated, we provide **12 months advance notice** - **Continued operation** - Deprecated versions continue functioning during the notice period - **Retirement** - After the notice period expires, the deprecated version is retired ## Change Log A complete, chronological history of changes to the Administration API Suite is published in the [Change Log](../changelog.mdx). Each entry records the affected API, the change type, and the version, and the list can be filtered by API. ## Change Notifications ### Non-Breaking Changes - **No advance notification** provided - **Changelog publication** - Changes are documented in the API changelog on the developer portal at the time they go live - **Location** - Changelog available on the respective API documentation page on developer.8x8.com ### Breaking Changes #### Standard Process (New Version) - **Changelog publication** - New version documented in the API changelog when released - **Version deprecation notice** - 12-month advance notice via email to administrative users when old versions are deprecated #### Emergency Changes (Unavoidable Breaking Change to Current Version) In rare circumstances where a breaking change cannot be avoided on the current API version (e.g., critical security vulnerabilities): - **Advance notice** - **90 days minimum** before change implementation - **Notification method** - Email sent to all administrative users with API key creation permissions - **Change details** - Comprehensive description of the change and required client modifications ## Client Implementation Guidance To ensure your integrations remain resilient to non-breaking changes, follow these best practices: ### Handling Unknown Attributes **Ignore unexpected fields in API responses:** ```json // API may add new fields at any time { "userId": "12345", "email": "user@example.com", "phoneNumberType": "MOBILE" // New field - your client should tolerate this } ``` Your client should process known fields and gracefully ignore any unexpected attributes. ### Get-Modify-Put Operations **Preserve all attributes when updating resources:** 1. **GET** the current resource state 2. **Modify** only the fields you need to change 3. **PUT** the entire resource back, including all fields (even new ones you don't recognize) **Example:** ```javascript // 1. GET the user const user = await getUser(userId); // 2. Modify only the fields you need user.email = "newemail@example.com"; // 3. PUT back the entire object (including any new fields) await updateUser(userId, user); ``` This pattern ensures compatibility when new attributes are added to resources. :::danger CRITICAL: Understanding PUT Semantics Administration APIs do NOT support partial updates via PATCH. PUT operations require the COMPLETE resource object. Be sure to familiarise yourself with the correct update pattern in the [Administration API Essentials - Understanding PUT Semantics](./suite-common.mdx#understanding-put-semantics) section to avoid unintended data loss. ::: ### Enum Value Handling **Handle unexpected enum values gracefully:** - Use defensive coding practices (default cases, unknown value handlers) - Don't fail when encountering new enum values you don't recognize - Log unknown values for future investigation but continue processing ## Support and Questions For questions about API changes or to provide feedback on this policy, please contact 8x8 Support. --- ## Administration API Reference :::warning BETA - Limited Access **These Administration APIs are currently in Beta testing.** API keys cannot be generated in Admin Console at this point. Only Beta program participants may use these APIs. ::: Welcome to the Administration API reference documentation. ## Available APIs ### User Management API The User Management API allows you to manage users within your organization. You can: - Retrieve paginated lists of users with filtering and sorting - Create new users asynchronously - Get detailed information for specific users - Update existing users - Delete users See the [User Management API Reference](/administration/reference/8-x-8-administration-user-management-api) documentation for more details. ### Ring Group Management API The Ring Group Management API enables you to programmatically manage ring groups across your 8x8 voice infrastructure. You can: - Search and filter ring groups with advanced query capabilities - Create ring groups with custom routing patterns - Retrieve detailed ring group configurations - Update ring group settings and member assignments - Delete ring groups See the [Ring Group Management API Reference](/administration/reference/8-x-8-administration-ring-group-management-api) documentation for more details. ### Site Management API The Site Management API allows you to manage sites (branches) within your organization. You can: - Search and filter sites across your organization - Create new sites with location and configuration details - Retrieve detailed site information - Update site settings and configurations - Delete sites See the [Site Management API Reference](/administration/reference/8-x-8-administration-site-management-api) documentation for more details. ### Phone Number Management API The Phone Number Management API provides read-only access to phone numbers in your organization. You can: - Retrieve paginated lists of phone numbers with filtering - Get detailed information for specific phone numbers See the [Phone Number Management API Reference](/administration/reference/8-x-8-administration-phone-number-management-api) documentation for more details. ### Address Management API The Address Management API enables you to manage addresses associated with sites. You can: - Search and filter addresses with query capabilities - Create new addresses for site locations - Retrieve detailed address information - Delete addresses See the [Address Management API Reference](/administration/reference/8-x-8-administration-address-management-api) documentation for more details. --- ## Contact Object Structure This section is dedicated to the Contact object structure, which is a central element in contact management. This guide will help you understand the detailed structure of the Contact object used across various endpoints, such as contact details retrieval, updates, and listings. > 📘 **Contact Types** > > In our system, contacts are categorized as follows: > > * **Corporate** or **Service** > > > > > > These are pre-configured or system-generated contacts that are essential for the functioning of various services or corporate operations. > > > > > > > > * **Company** > > > > > > These are user-defined or manually added contacts that represent individuals or company entities outside of the core system functionalities. > > > > > > > > This API allows modifications to company contacts only. You can create, update or delete company contacts as needed. Please note that modifications to other types of contacts are not supported > > ## Contact JSON Object Structure Below is the detailed JSON structure of the Contact object. This structure is utilized in several API endpoints related to contact operations. ```json { "id": "2h_JNsaISleZWA36GRh3YQ", "assignedUserId": "2h_JNsaISleZWA36GRh3YQ", "branchId": "ovYzzgfDSDqolA3RbhWjbw", "branchName": "Tech Support Division", "companyName": "8x8 Inc.", "contactType": "corporate", "createdTimestamp": 1621341000000, "customerId": "0016C00000VM1BeQAL", "department": "Support", "displayWhenNoExtension": true, "firstName": "James", "hideInAA": false, "jobTitle": "Agent", "lastName": "Miller", "locale": "en_US", "middleName": "Edward", "name": "James Edward Miller", "nickName": "Jim", "pbxId": "bhjLT03CTJuVwgAy9y3DOQ", "pbxName": "qmsarealenv1", "pictureHash": "a046a853f0e131f18001d1174d8588ff76172a1350ba5ff1ba081caa470f6e1e", "timeZone": "America/Los_Angeles", "updatedTimestamp": 1678182231575, "addresses": [ { "id": 23071, "apartmentNumber": null, "city": "San Jose", "country": "United States", "county": "Santa Clara", "notes": "8x8 Headquarters", "postalCode": "95131", "primary": true, "purposeType": "WORK", "state": "California", "streetName": "1st St", "streetNumber": "675" } ], "emails": [ { "id": 86533, "email": "contact@example.com", "primary": true, "purposeType": "WORK" } ], "extensions": [ { "id": 52400, "branchId": "ovYzzgfDSDqolA3RbhWjbw", "branchName": "ContactSite", "contactId": "2h_JNsaISleZWA36GRh3YQ", "displayInDirectory": true, "extension": "60000001", "extensionType": "CC", "fqExtension": "1460000001", "pbxId": "bhjLT03CTJuVwgAy9y3DOQ", "pbxName": "qmsarealenv1", "subscriptionId": "aK60JcBERnKIya9Rt7BCxA", "subscriptionType": "UE" } ], "phones": [ { "id": 48073, "phone": "04029511367", "primary": true, "purposeType": "WORK", "source": "EXTERNAL" }, { "id": 48074, "phone": "0756124412", "primary": false, "purposeType": "HOME", "source": "EXTERNAL" } ], "tags": [ { "id": 25672, "name": "customField2", "value": "value2" }, { "id": 25673, "name": "customField1", "value": "value_updated" }, { "id": 25674, "name": "customField3", "value": "value3" } ] } ``` > 🚧 **Legacy Field Usage** > > In the latest version of our application, we have retained certain fields from the previous system iteration, such as **subscriptionId** and **contactRecordId**, for archival and reference purposes. These fields are crucial in preserving historical data linkages and ensuring continuity. > > **We advise against using these legacy fields for current decision-making or feature development** > > ## Detailed Field Descriptions ### Basic Information | Name | Type | Description | Example | Applicability | Restrictions | | --- | --- | --- | --- | --- | --- | | id\* | string | Unique identifier for the contact | null (auto-generated) | All | Read Only | | assignedUserId | string | Identifier for the user to whom the contact is assigned | bhjT03CtUvWgAy9b3DOQ | Corporate | Read Only | | branchId | string | Identifier for the site where the contact is located | br_301 | All | Max 64 chars | | branchName | string | Name of the site where the contact is located | Tech Support Division | All | Max 128 chars | | companyName | string | Name of the company the contact is associated with | 8x8 Inc. | Company | Max 128 chars | | contactType\* | string | Type of contact, e.g., company | company | All | Read Only Enum: company, corporate, service | | customerId\* | string | Identifier for the customer to whom the contact belongs | 0016C00000VM1BeQAX | All | Max 32 chars | | department | string | Department within the company where the contact works | Support | Company, Corporate | Max 100 chars | | displayWhenNoExtension | boolean | Flag to control visibility of a corporate contact that does not have an extension (i.e. a user with no license). This setting is ignored for corporate contacts that have one more more extensions (extensions[x].displayInDirectory will be used instead) | false | Corporate | Read Only | | firstName | string | First name of the contact | Alicia | Company, Corporate | Max 128 chars | | jobTitle | string | Professional title of the contact | Account Manager | Corporate | Read Only | | lastName | string | Last name of the contact | Rodriguez | Company, Corporate | Max 30 chars | | locale | string | Locale setting representing the contact's language and region format | en_US | Company, Corporate | This expects a language code(en-US), or a string representation(en_US) | | location | string | Physical or office location of the contact | New York Office | Company | Max 128 chars | | middleName | string | Middle name of the contact | B. | Company, Corporate | Max 30 chars | | nickName | string | Nickname or informal name used for the contact | Ali | Company, Corporate | Max 30 chars | | pbxId | string | Identifier for the PBX associated with the contact | tVK8Vd5Aj1yskcl_-_13A | All | Max 32 chars | | pbxName | string | Name of the PBX associated with the contact | voedidionworkflow25 | All | Max 32 chars | | pictureHash | string | A unique hash value representing the picture, used for verification purposes. | a046a853f0e131f18001d1174d | Company, Corporate | Read Only | | subscriptionId | string | Unique identifier for the subscription | 0_0qy7cCMFK1HEXLZZA | Service | Read Only | | subscriptionName | string | The name of the subscription plan or service | Helpdesk | Service | Read Only | | subscriptionType | string | Indicates the service type associated with this extension. It defines which service or functionality the extension is currently linked to or utilizing. | CQ | Service | Read Only Supported Values: UE, VCCE, RG, CQ, AA | | subscriptionUserId | string | Identifier for the user associated with the subscription | 0eDDyAIIQU1Sb771BB23g | Service | Read Only | | timeZone | string | Time zone where the contact is located | America/New_York | Company, Corporate | Accepts region-based zone IDs only (e.g., "America/New_York") | ### Addresses | Name | Type | Description | Example | Restrictions | | --- | --- | --- | --- | --- | | apartmentNumber | string | The specific apartment number within a building or complex | 102 | Max 5 digits | | city | string | The city of the contact’s address | San Jose | Max 64 chars | | country | string | The nation where the address is located | United States | Max 64 chars | | county | string | The county of the contact's address | Santa Clara | Max 64 chars | | notes | string | Additional notes about the address | 8x8 Headquarters | Max 128 chars | | postalCode | string | The postal or ZIP code for the address | 95131 | Max 16 chars | | primary | boolean | Indicates if this is the primary address for the contact | true | | | purposeType | string | The intended use of the address (e.g., WORK, HOME) | WORK | Supported Values: HOME, WORK, OTHER | | state | string | The state or region in which the contact is located | California | Max 64 chars | | streetName | string | The name of the street for the address | 1st St | Max 64 chars | | streetNumber | string | The house or building number | 675 | Max 8 chars | ### Emails | Name | Type | Description | Example | Restrictions | | --- | --- | --- |---------------------------------| --- | | email\* | string | The email address associated with the contact | [contact@example.com](mailto:contact@example.com) | Valid email format; must be unique | | primary\* | boolean | Indicates if this is the primary email address for the contact | true | Only one email can be designated as primary | | purposeType\* | string | The intended use of the email address | WORK | Supported Values: HOME, WORK, OTHER | ### Phones | Name | Type | Description | Example | Restrictions | | --- | --- | --- | --- | --- | | phone\* | string | Phone number of the contact. Recommended format: E.164 without punctuation | +18005551234 | Valid phone format; must be unique | | primary\* | boolean | Indicates if this is the primary phone number for the contact | true | Only one phone can be designated as primary | | purposeType\* | string | The intended use of the phone number | WORK | Supported Values: HOME, HOME_FAX, WORK, WORK_FAX, MOBILE, PAGER, OTHER | | source | string | Origin of the phone number | EXTERNAL | Supported Value: EXTERNAL | ### Tags | Name | Type | Description | Example | Restrictions | | --- | --- | --- | --- | --- | | name\* | string | The name or key of the tag | customField1 | Limited to 10 values like **customField1** through **customField10.** | | value | string | The value assigned to the tag | value1 | Max 254 chars | > 📘 **Mandatory Attributes** > > Fields marked with an asterisk (\*) are mandatory for creating a contact. These attributes are essential and a contact cannot exist without them. > > ### Extensions | Name | Type | Description | Example | Restrictions | | --- | --- | --- | --- | --- | | branchId | string | Unique identifier for the branch | ovYzzgfDSDqolA3RbhWjbw | Read Only | | branchName | string | Name of the branch where the extension is located | DowntownBranch | Read Only | | contactId | string | Unique identifier for the associated contact | 2h_JNsaISleZWA36GRh3YQ | Read Only | | displayInDirectory | boolean | Indicates if the extension is visible in the directory | true | Read Only | | extension | string | Extension number | 60000001 | Read Only | | extensionType | string | Indicates whether it is a UC or CC extension | CC | Read Only | | fqExtension | string | Fully qualified extension number | 1460000001 | Read Only | | hideInAA | boolean | Flag to hide the contact in the Auto Attendant | true | Read Only | | pbxId | string | Unique identifier for the PBX | bhjLT03CTJuVwgAy9y3DOQ | Read Only | | pbxName | string | Name of the PBX system associated with the extension | qmsarealenv1 | Read Only | | subscriptionId | string | Unique identifier for the subscription | aK60JcBERnKIya9Rt7BCxA | Read Only | | subscriptionType | string | Type of subscription | UE | Read Only | > 🚧 **Extensions Operational Restrictions** > > Considered system-generated resources, we restrict modifications to extensions to maintain system integrity. > > ### Visibility Flags Information | Name | Type | Description | Level | Example | | --- | --- | --- | --- | --- | | displayInDirectory | boolean | Indicates if the extension is visible in the directory | Extension | true | | displayWhenNoExtension | boolean | Indicates whether to display the contact when there is no extension number associated | Contact | false | | hideInAA | boolean | Determines if the contact should be hidden in the Auto Attendant | Extension | true | --- ## Contact Management Our Contact API allows you to add new contacts into the system. This section guides you through the steps to create a contact. Before you start, make sure you have an API key to authenticate your requests. For comprehensive information about the object structure, including restrictions, please refer to the [Object Structure Guide](/administration/docs/contact-object-structure-guide). **Endpoint for Contact Management**: `https://api.8x8.com/directory-contacts/api/v3/contacts` ## 1. Obtain API Key for Contact Management Product To use the Contact Search endpoint, you must obtain a **Contact Management API Key**. This key is required for any requests that create, update, or delete contact information, i.e., POST, PUT, DELETE methods. [How to get API Keys](/analytics/docs/how-to-get-api-keys) ## 2. Create Contact Once authenticated, you can create a new contact by sending a `POST` request to the Contact API with the necessary information. ### HTTP Request `POST https://api.8x8.com/directory-contacts/api/v3/contacts` ### Request Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Content-Type | ✓ | This indicates that the request body is in JSON format | application/json | | x-api-key | ✓ | Pass the API key returned from Admin Console for Contact Management product | romc_MmFmMTI3sowe | ### Body [Detailed Field Descriptions](/administration/docs/contact-object-structure-guide) ```json { "companyName":"8x8, Inc.", "contactType":"company", "customerId":"0016C00000VM1BeQAL", "department":"Research and Development", "firstName":"John", "jobTitle":"Software Eng.", "lastName":"Doe", "locale":"en_US", "location":"San Jose, CA", "nickName":"johnny", "pbxId":"bhjLT03CTJuVwgAy9y3DOQ", "pbxName":"qmsarealenv1", "timeZone":"America/Los_Angeles", "addresses":[ { "city":"San Jose", "country":"United States", "county":"Santa Clara", "notes":"8x8 Headquarters", "postalCode":"95131", "primary":true, "purposeType":"WORK", "state":"California", "streetName":"1st St", "streetNumber":"675" } ], "emails":[ { "email":"test.user@company.com", "primary":true, "purposeType":"WORK" } ], "phones":[ { "phone":"+18005551234", "primary":true, "purposeType":"WORK", "source":"EXTERNAL" } ], "tags":[ { "name":"customField1", "value":"value1" } ] } ``` ### Response A successful creation will yield a 200 status code and a response body with the details of the new contact, including a contactId. Save the contactId for any future reference. ```json { "id": "c3697cef-5e57-41cc-8720-6db6e8e2a977", "companyName":"8x8, Inc.", "contactType":"company", "customerId":"0016C00000VM1BeQAL", "department":"Research and Development", "firstName":"John", "jobTitle":"Software Eng.", "lastName":"Doe", "locale":"en_US", "location":"San Jose, CA", "nickName":"johnny", "pbxId":"bhjLT03CTJuVwgAy9y3DOQ", "pbxName":"qmsarealenv1", "timeZone":"America/Los_Angeles", "addresses":[ { "id": 4038630, "city":"San Jose", "country":"United States", "county":"Santa Clara", "notes":"8x8 Headquarters", "postalCode":"95131", "primary":true, "purposeType":"WORK", "state":"California", "streetName":"1st St", "streetNumber":"675" } ], "emails":[ { "id": 5106357, "email":"test.user@company.com", "primary":true, "purposeType":"WORK" } ], "phones":[ { "id": 3115234, "phone":"+18005551234", "primary":true, "purposeType":"WORK", "source":"EXTERNAL" } ], "tags":[ { "id": 3248976, "name":"customField1", "value":"value1" } ] } ``` ## 3. Modify Contact After creating a contact, you may need to update its information. To modify an existing contact, use a `PUT` request to the Contact API with the updated details. ### HTTP Request `PUT https://api.8x8.com/directory-contacts/api/v3/contacts/{contactId}` Replace `{contactId}` with the unique identifier of the contact you wish to update. ### Request Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Content-Type | ✓ | This indicates that the request body is in JSON format | application/json | | x-api-key | ✓ | Pass the API key returned from Admin Console for Contact Management product | romc_MmFmMTI3sowe | ### Body [Detailed Field Descriptions](/administration/docs/contact-object-structure-guide) ```json { "id": "c3697cef-5e57-41cc-8720-6db6e8e2a977", "companyName":"8x8, Inc.", "contactType":"company", "customerId":"0016C00000VM1BeQAL", "department":"Marketing", "firstName":"John", "jobTitle":"Marketing Manager", "lastName":"Doe", "locale":"en_US", "location":"San Jose, CA", "nickName":"johnny", "pbxId":"bhjLT03CTJuVwgAy9y3DOQ", "pbxName":"qmsarealenv1", "timeZone":"America/Los_Angeles", "addresses":[ { "city":"New York", "country":"United States", "county":"New York", "notes":"Secondary Office Location", "postalCode":"10001", "primary":true, "purposeType":"WORK", "state":"New York", "streetName":"5th Ave", "streetNumber":"350" } ], "emails":[ { "email":"test.user@company.com", "primary":true, "purposeType":"WORK" } ], "phones":[ { "phone":"+18003351234", "primary":true, "purposeType":"WORK", "source":"EXTERNAL" } ], "tags":[ { "name":"customField1", "value":"value2" } ] } ``` Provide the complete set of fields for the resource. Any fields not included in the request will be set to their default values or nullified. ### Response A successful creation will yield a 200 status code and a response body with the details of the updated contact. I'm A tab ```json { "id": "c3697cef-5e57-41cc-8720-6db6e8e2a977", "companyName":"8x8, Inc.", "contactType":"company", "customerId":"0016C00000VM1BeQAL", "department":"Marketing", "firstName":"John", "jobTitle":"Marketing Manager", "lastName":"Doe", "locale":"en_US", "location":"San Jose, CA", "nickName":"johnny", "pbxId":"bhjLT03CTJuVwgAy9y3DOQ", "pbxName":"qmsarealenv1", "timeZone":"America/Los_Angeles", "addresses":[ { "id": 4038640, "city":"New York", "country":"United States", "county":"New York", "notes":"Secondary Office Location", "postalCode":"10001", "primary":true, "purposeType":"WORK", "state":"New York", "streetName":"5th Ave", "streetNumber":"350" } ], "emails":[ { "id": 3115834, "email":"test.user@company.com", "primary":true, "purposeType":"WORK" } ], "phones":[ { "id": 5106367, "phone":"+18003351234", "primary":true, "purposeType":"WORK", "source":"EXTERNAL" } ], "tags":[ { "id": 3228976, "name":"customField1", "value":"value2" } ] } ``` ### Non-updatable Fields The fields in the table below are read only and should not be included in any PUT request. | Field | Description | | --- | --- | | createdTimestamp | Timestamp when the contact was created | | updatedTimestamp | Timestamp when the contact was last updated | ## 4. Delete Contact To remove a contact from our system, use the DELETE request with the specific contact's ID. This action is irreversible, so ensure that the contact is indeed meant to be deleted. ### HTTP Request `DELETE https://api.8x8.com/directory-contacts/api/v3/contacts/{contactId}` Replace `{contactId}` with the unique identifier of the contact you wish to delete. ### Request Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Content-Type | ✓ | This indicates that the request body is in JSON format | application/json | | x-api-key | ✓ | Pass the API key returned from Admin Console for Contact Management product | romc_MmFmMTI3sowe | ### Body No request body is needed for a delete operation. ### Response A successful deletion will yield a 200 status code. The response body will typically be empty, indicating that the contact has been successfully removed from the system. > ⚠️ **Important: Deleting a Contact** > > Deleting a contact is a permanent action and cannot be undone. Please confirm the contact ID before proceeding with this operation to avoid unintended deletions. > > ## 5. Contact Search for Retrieval and Queries While the ContactApp Product excels in managing contact details, for retrieving or searching for specific contacts, it's recommended to utilize the dedicated `Contact Search` API Key within the same service. This feature is specifically optimized for efficient and precise querying of contact data. For detailed instructions on how to use this functionality, please refer to the documentation [here](/administration/docs/search-contacts). ## Rate Limiting The Contact Search and Contact Management APIs are limited to **60 requests per minute**, in a rolling window, across all the keys under your customer account. If this limit is exceeded then a **429 Too Many Requests** response code will be returned. --- ## Introduction(Docs) :::warning BETA - Limited Access **These Administration APIs are currently in Beta testing.** API keys cannot be generated in Admin Console at this point. Only Beta program participants may use these APIs. ::: Welcome to the 8x8 Administration Developer hub. You'll find comprehensive guides and documentation to help you manage administrative tasks and configurations. This section contains APIs and guides for administrative tasks and configurations across the 8x8 platform. You will find [Guides](/administration/docs) which describe the administrative APIs and tasks, and [API References](/administration/reference) which allow you to explore the APIs in detail. ## Available APIs ### User Management API Manage user accounts within your organization programmatically. See the [User Management API Guide](/administration/docs/user-management-api-guide). ### Ring Group Management API Create and manage ring groups for call distribution across teams. See the [Ring Group Management API Guide](/administration/docs/ring-group-management-api-guide). ### Phone Number Management API Retrieve and search phone numbers assigned to your organization. See the [Phone Number Management API Guide](/administration/docs/phone-number-management-api-guide). ### Site & Address Management API Manage sites (branches) and their associated addresses within your 8x8 organization. See the [Site Management API Guide](/administration/docs/site-management-api-guide). --- ## Phone Number Management API Guide import TabbedExternalCodeSample from '@site/docusaurus/components/TabbedExternalCodeSample'; # Phone Number Management API Guide **API Version**: 1.0 | **Last Updated**: January 15, 2026 | **Part of**: [Administration API Suite](./suite-common.mdx) :::warning BETA - Limited Access **These Administration APIs are currently in Beta testing.** API keys cannot be generated in Admin Console at this point. Only Beta program participants may use these APIs. ::: ## Table of Contents 1. [Overview](#overview) 2. [Prerequisites & Authentication](#prerequisites--authentication) 3. [Getting Started](#getting-started) 4. [Core Concepts](#core-concepts) 5. [Use Cases](#use-cases) 6. [API Reference](#api-reference) 7. [API-Specific Error Scenarios](#api-specific-error-scenarios) 8. [API-Specific Troubleshooting](#api-specific-troubleshooting) 9. [Additional Resources](#additional-resources) --- ## Overview The **Phone Number Management API** is part of the **[Administration API Suite](./suite-common.mdx)**. It provides read-only access to your organization's phone number inventory, enabling you to discover available phone numbers, understand number assignments, and export your phone number estate for integration with external systems. ### What This API Does This API enables you to: - **Search and filter phone numbers** in your inventory by status, country, category, and other attributes - **Retrieve detailed metadata** about specific phone numbers including formatting, origin, and assignment status - **Identify available phone numbers** for assignment to users or ring groups via the User Management and Ring Group Management APIs - **Export phone number data** for reporting, auditing, or integration with external provisioning systems ### When to Use This API Use the Phone Number Management API when you need to: - Find available phone numbers before assigning them to users or ring groups - Audit your organization's phone number inventory - Track phone number usage and availability across your organization - Build automated provisioning workflows that require phone number discovery - Export phone number data to external systems This API is designed for **discovery and retrieval** of phone number information. For number acquisition (claiming from 8x8 stock or porting from other carriers), use the Phone Numbers page in the 8x8 Admin Console. ### Relationship to Other APIs The Phone Number Management API works alongside other Administration APIs: - **User Management API**: Assign discovered phone numbers to user accounts - **Ring Group Management API**: Assign discovered phone numbers to ring groups - **Phone Number Management API** (this API): Discover available phone numbers and retrieve metadata **Typical workflow**: Use this API to find available phone numbers, then use User Management or Ring Group Management APIs to assign those numbers to services. --- ## Prerequisites & Authentication Before using the Phone Number Management API, review the **[Administration API Essentials](./suite-common.mdx)**, which covers: - **Prerequisites**: Account requirements, API credential acquisition, technical requirements - **Getting Started**: Step-by-step credential setup and testing - **Authentication**: API key authentication with x-api-key header - **API Versioning**: Version negotiation with Accept header - **Common Patterns**: Asynchronous operations, pagination, filtering, sorting - **Error Handling**: RFC 7807 format and common error scenarios - **Rate Limiting**: Request limits and handling strategies - **Best Practices**: Performance, security, and data consistency - **Troubleshooting**: Common issues and debugging steps - **Support Resources**: Contact information and escalation procedures ### Phone Number Management API Specifics **Accept Header** for this API: ```text Accept: application/vnd.phonenumbers.v1+json ``` **Required API Products** (when creating API key in Admin Console): - **UC & CC Number Admin**: Required for phone number data access **Phone Number Management-Specific Prerequisites**: - Access to 8x8 Admin Console to verify your phone number inventory - Understanding of E.164 phone number format (international standard) - Familiarity with your organization's phone number assignment policies --- ## Getting Started This quickstart demonstrates how to retrieve your organization's phone number inventory and filter for available numbers in a specific country. ### Your First Phone Number Query **Request**: Make a GET request to `/phone-numbers` with a filter for available US numbers: ```http GET /phone-numbers?filter=status==AVAILABLE;country==US&pageSize=100 ``` **Expected Response**: The API returns a paginated list of phone numbers matching your filter: ```json { "data": [ { "phoneNumber": "+14085551234", "nationalFormattedNumber": "(408) 555-1234", "country": "US", "category": "LOCAL", "origin": "CLAIMED", "status": "AVAILABLE" }, { "phoneNumber": "+14155559876", "nationalFormattedNumber": "(415) 555-9876", "country": "US", "category": "TOLL_FREE", "origin": "PORTING", "status": "AVAILABLE" } ], "pagination": { "pageSize": 100, "hasMore": false, "filter": "status==AVAILABLE;country==US" }, "_links": { "self": { "href": "https://api.8x8.com/admin-provisioning/phone-numbers?filter=status==AVAILABLE;country==US&pageSize=100" } } } ``` ### Next Steps Now that you've retrieved your phone number inventory, explore: - [Filtering by phone number attributes](#filtering-phone-numbers) to find specific numbers - [Understanding phone number status](#phone-number-status) for provisioning workflows - [Retrieving specific phone numbers](#get-phone-numbersphonenumber) by their E.164 value --- ## Core Concepts ### Phone Numbers in E.164 Format All phone numbers in this API use **E.164 international format**: a plus sign (`+`) followed by the country code and number with no spaces or special characters. **Examples**: - United States: `+14085551234` - United Kingdom: `+442071234567` - Australia: `+61212345678` The API also provides a `nationalFormattedNumber` field with country-specific formatting for display purposes (e.g., `"(408) 555-1234"` for US numbers). ### Phone Number Status Phone numbers in your inventory have one of four status values that indicate their current state: | Status | Description | Use For | |--------|-------------|---------| | `AVAILABLE` | Ready to be assigned to a service (user or ring group). Most common status for provisioning workflows. | Searching for numbers to assign to users or ring groups | | `ASSIGNED` | Currently in use by a service (user, ring group, or other telephony resource). | Auditing which numbers are actively in use | | `PRE_PORTING` | Linked to a temporary 8x8 number, but porting process has not started. Includes `portingNumber` and `temporaryNumber` fields. | Preparing for number porting | | `PORTING` | Porting process actively underway. Will become `AVAILABLE` or `ASSIGNED` once complete. | Tracking porting progress | ### Number Origin The `origin` field indicates how the number entered your inventory: | Origin | Description | |--------|-------------| | `CLAIMED` | Claimed directly from 8x8's available phone number stock through Admin Console or provisioning process. | | `PORTING` | Being ported (or was ported) from another carrier to 8x8. | ### Number Category The `category` field indicates the type of phone number: | Category | Description | |----------|-------------| | `LOCAL` | Standard local phone number tied to a specific geographic area and country. | | `TOLL_FREE` | Toll-free number that allows callers to reach you without incurring charges (e.g., 1-800 numbers in the US). | ### Country Codes The `country` field uses **[ISO 3166-1 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)** two-character country codes: - `US`: United States - `GB`: United Kingdom - `AU`: Australia - `CA`: Canada - `DE`: Germany --- ## Use Cases ### Use Case 1: Find Available Phone Numbers for User Assignment **Scenario**: You're building an automated user provisioning workflow and need to discover available phone numbers in a specific country before assigning them to new users. **Implementation Pattern**: 1. Query the Phone Number Management API to find available numbers filtered by country and category 2. Select an appropriate number from the results 3. Use the User Management API to assign the selected number to a user **Expected Outcome**: You receive a list of available phone numbers matching your criteria, ready for assignment via the User Management API. **Next Step**: Pass the selected `phoneNumber` value (e.g., `+14085551234`) to the User Management API when creating or updating a user. --- ### Use Case 2: Audit Phone Number Usage by Status **Scenario**: You need to generate a report showing how many phone numbers are available, assigned, or in porting status for capacity planning and inventory management. **Implementation Pattern**: 1. Query the Phone Number Management API multiple times with different status filters 2. Count results for each status category 3. Aggregate data for reporting or dashboard display **Expected Outcome**: You receive counts for each status category, enabling capacity planning and inventory management decisions. --- ### Use Case 3: Export Phone Number Inventory to External System **Scenario**: You need to export your complete phone number inventory to an external CRM, reporting system, or database for integration with other business processes. **Implementation Pattern**: 1. Query the Phone Number Management API without filters to retrieve all phone numbers 2. Handle pagination using scroll IDs to iterate through large datasets 3. Transform data to required format for external system 4. Load data into target system **Expected Outcome**: Complete phone number inventory exported to external system in required format. --- ### Use Case 4: Verify Phone Number Availability Before Assignment **Scenario**: Before assigning a specific phone number to a user or ring group, you need to verify it exists in your inventory and is currently available. **Implementation Pattern**: 1. Use the GET `/phone-numbers/\{phoneNumber\}` endpoint to retrieve specific number 2. Check the `status` field to verify it's `AVAILABLE` 3. Proceed with assignment if available, or select alternative number if not **Expected Outcome**: Confirmation of phone number status before proceeding with assignment operation. --- ## API Reference The Phone Number Management API provides two endpoints for phone number discovery and retrieval. ### Endpoints Summary | Endpoint | Method | Purpose | |----------|--------|---------| | `/phone-numbers` | GET | Search and list phone numbers with filtering, sorting, and pagination | | `/phone-numbers/\{phoneNumber\}` | GET | Retrieve specific phone number by E.164 value | ### GET /phone-numbers **Purpose**: Search for phone numbers in your inventory with optional filtering, sorting, and scroll-based pagination. **Query Parameters**: | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `pageSize` | integer | No | Number of items per page (default: 100, max: 100) | | `scrollId` | string | No | Scroll identifier for retrieving next page (obtained from previous response) | | `filter` | string | No | RSQL filter expression (see [Filtering](#filtering-phone-numbers) below) | | `sort` | string | No | Sort expression - single attribute only (see [Sorting](#sorting-results) below) | **Response**: Returns a paginated list of phone numbers matching your criteria. **See**: [Administration API Essentials - Pagination](./suite-common.mdx#pagination) for scroll-based pagination details. #### Filtering Phone Numbers The API supports RSQL filtering on the following phone number attributes: **Filterable Fields**: - `phoneNumber`: The E.164 phone number value (e.g., `+14085551234`) - `country`: ISO 3166-1 alpha-2 country code (e.g., `US`, `GB`, `AU`) - `category`: Number category (`LOCAL` or `TOLL_FREE`) - `origin`: Number origin (`CLAIMED` or `PORTING`) - `status`: Current status (`AVAILABLE`, `ASSIGNED`, `PORTING`, `PRE_PORTING`) **Common Filter Examples**: ```text status==AVAILABLE country==US status==AVAILABLE;country==US category==LOCAL status==AVAILABLE;country==US;category==LOCAL phoneNumber==+14085551234 origin==CLAIMED;status==AVAILABLE ``` **See**: [Administration API Essentials - Filtering with RSQL](./suite-common.mdx#filtering-with-rsql) for complete RSQL syntax and operators. #### Sorting Results The API supports sorting on a **single attribute only** (unlike some other APIs that support multi-attribute sorting). **Sortable Fields**: - `phoneNumber` - `country` - `category` **Sort Syntax**: - Ascending: `phoneNumber` or `+phoneNumber` - Descending: `-phoneNumber` **Examples**: ```text sort=phoneNumber (ascending by phone number) sort=-country (descending by country) sort=category (ascending by category) ``` **Note**: Only one sort attribute can be specified per request. Attempting to sort by multiple attributes will result in a validation error. **See**: [Administration API Essentials - Sorting Results](./suite-common.mdx#sorting-results) for general sorting conventions. ### GET /phone-numbers/\{phoneNumber\} **Purpose**: Retrieve detailed information about a specific phone number by its E.164 value. **Path Parameters**: | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | `phoneNumber` | string | Yes | Phone number in E.164 format (e.g., `+14085551234`) | **Response**: Returns complete metadata for the specified phone number. **Error Scenarios**: - `400 Bad Request`: Phone number not in valid E.164 format - `404 Not Found`: Phone number does not exist in your inventory - `403 Forbidden`: Insufficient permissions or customer ID mismatch ### Phone Number Response Schema All endpoints return phone number objects with these fields: | Field | Type | Description | |-------|------|-------------| | `phoneNumber` | string | E.164 formatted phone number (e.g., `+14085551234`) | | `nationalFormattedNumber` | string | Country-specific formatted number (e.g., `"(408) 555-1234"`) | | `country` | string | ISO 3166-1 alpha-2 country code | | `category` | string | Number type: `LOCAL` or `TOLL_FREE` | | `origin` | string | Number source: `CLAIMED` or `PORTING` | | `status` | string | Current state: `AVAILABLE`, `ASSIGNED`, `PORTING`, `PRE_PORTING` | | `portingNumber` | string | (Optional) During porting: the number being ported | | `temporaryNumber` | string | (Optional) During porting: the temporary 8x8 number | **Full Schema**: See the [OpenAPI Specification](../../../docs_oas/administration/phonenumber-api-v1.yaml) for complete schema definitions. --- ## Common Patterns This API follows standard patterns covered in the **[Administration API Suite Common Documentation](./suite-common.mdx)**: - **Authentication**: See [Administration API Essentials - Authentication](./suite-common.mdx#authentication) for API key usage and header format - **Pagination**: See [Administration API Essentials - Pagination](./suite-common.mdx#pagination) for scroll-based pagination with `scrollId` parameter - **Filtering**: See [Administration API Essentials - Filtering with RSQL](./suite-common.mdx#filtering-with-rsql) for RSQL syntax, operators, and examples - **Sorting**: See [Administration API Essentials - Sorting Results](./suite-common.mdx#sorting-results) for general sorting conventions - **Rate Limiting**: See [Administration API Essentials - Rate Limiting](./suite-common.mdx#rate-limiting) for policies and best practices - **Error Handling**: See [Administration API Essentials - Error Handling](./suite-common.mdx#error-handling) for status codes and recovery strategies ### API-Specific Pattern Notes **Single-Attribute Sorting Only**: Unlike some Administration APIs that support multi-attribute sorting, the Phone Number Management API only supports sorting by a single attribute per request. Attempting to specify multiple sort attributes will result in a `400 Bad Request` error. **Synchronous Operations**: All phone number retrieval operations are synchronous. You receive results immediately in the response (no asynchronous polling required). **E.164 Format**: Always use the `phoneNumber` field value (E.164 format like `+14085551234`) when passing numbers to other APIs, not the `nationalFormattedNumber`. For general best practices, see [Administration API Essentials - Best Practices](./suite-common.mdx#best-practices). --- ## API-Specific Error Scenarios This section covers error scenarios **specific to the Phone Number Management API**. For common HTTP status codes and generic error handling, see **[Administration API Essentials - Error Handling](./suite-common.mdx#error-handling)**. ### Invalid E.164 Format (400 Bad Request) **When**: Phone number in path parameter is not in valid E.164 format **Example**: `GET /phone-numbers/4085551234` (missing `+` prefix) **Response**: ```json { "status": 400, "title": "Invalid phone number", "detail": "Invalid phone number 4085551234", "errors": [ { "field": "phoneNumber", "code": "VALIDATION_ERROR", "message": "Invalid phone number 4085551234" } ] } ``` **Resolution**: Ensure phone number includes `+` prefix and country code (e.g., `+14085551234`) #### Invalid Filter Syntax (400 Bad Request) **When**: RSQL filter expression has syntax errors **Example**: `?filter=status=AVAILABLE` (using `=` instead of `==`) **Response**: ```json { "status": 400, "title": "Validation error", "errors": [ { "field": "filter", "code": "VALIDATION_ERROR", "message": "Invalid filter syntax" } ] } ``` **Resolution**: Use correct RSQL syntax with `==` for equality comparisons. See [Filtering](#filtering-phone-numbers) for examples. #### Invalid Sort Parameter (400 Bad Request) **When**: Sort parameter references an invalid field or attempts multi-attribute sorting **Response**: ```json { "status": 400, "title": "Validation error", "errors": [ { "field": "sort", "code": "VALIDATION_ERROR", "message": "Invalid sort parameter" } ] } ``` **Resolution**: Use a single sortable field (`phoneNumber`, `country`, or `category`) #### Invalid Scroll ID (400 Bad Request) **When**: Scroll ID from previous response is malformed or expired **Response**: ```json { "status": 400, "title": "Validation error", "errors": [ { "field": "scrollId", "code": "VALIDATION_ERROR", "message": "Invalid scroll identifier" } ] } ``` **Resolution**: Ensure you're using the `nextScrollId` from the most recent response. Scroll IDs have a limited lifetime - restart pagination if expired. #### Conflicting Query Parameters (400 Bad Request) **When**: Attempting to use `scrollId` with other first-page query parameters **Example**: `?scrollId=abc123&filter=status==AVAILABLE` (mixing scrollId with filter) **Response**: ```json { "status": 400, "title": "Conflicting query parameters", "errors": [ { "code": "CONFLICTING_QUERY_PARAMETER", "message": "Cannot use scrollId with other first-page query parameters" } ] } ``` **Resolution**: Use `scrollId` only for continuation requests. Apply filters only on the first page. #### Phone Number Not Found (404 Not Found) **When**: Requested phone number does not exist in your organization's inventory **Example**: `GET /phone-numbers/+14085551234` (number not in inventory) **Response**: ```json { "status": 404, "title": "Phone number not found", "errors": [ { "code": "NOT_FOUND", "message": "Phone number not found: customerId: CUST123 phoneNumber: +14085551234" } ] } ``` **Resolution**: Verify the phone number exists in your inventory. The number may belong to another customer or may not have been provisioned yet. --- ## API-Specific Troubleshooting This section covers issues **specific to the Phone Number Management API**. For authentication, rate limiting, and other common issues, see **[Administration API Essentials - Troubleshooting](./suite-common.mdx#troubleshooting)**. ### No Available Phone Numbers Found **Symptom**: Query with `filter=status==AVAILABLE` returns empty results **Possible Causes**: 1. All phone numbers in your inventory are currently assigned 2. Filter is too restrictive (e.g., specific country + category combination has no matches) 3. Phone numbers have not been provisioned to your organization yet **Resolution**: 1. Remove or relax filters to see if any numbers exist: `GET /phone-numbers` 2. Check for numbers in other countries or categories 3. Verify phone numbers have been claimed or ported via Admin Console ### Scroll ID Expired During Pagination **Symptom**: `400 Bad Request` with `VALIDATION_ERROR` on scroll ID after some time **Cause**: Scroll IDs have a limited lifetime (typically 5-10 minutes) **Resolution**: 1. Complete pagination quickly to avoid expiration 2. If scroll ID expires, restart pagination from the beginning 3. For large exports, consider breaking into multiple smaller queries by filtering ### Phone Number Format Rejected **Symptom**: `400 Bad Request` when querying specific phone number by ID **Cause**: Phone number not in E.164 format (missing `+`, has spaces or special characters) **Resolution**: Ensure phone numbers: - Start with `+` sign - Include country code - Have no spaces, dashes, or parentheses - Example: `+14085551234` not `(408) 555-1234` or `4085551234` ### Cannot Sort By Multiple Attributes **Symptom**: `400 Bad Request` when attempting to sort by multiple fields **Cause**: This API only supports single-attribute sorting **Resolution**: 1. Choose the most important sort attribute 2. Implement additional sorting client-side if needed 3. Example: `sort=country` (then sort by phoneNumber in your code) --- ## Additional Resources **API Documentation**: - [Administration API Essentials](./suite-common.mdx) - [OpenAPI Specification](/administration/phonenumber-api-v1.yaml) - [8x8 Admin Console](https://admin.8x8.com) **Technical References**: - [RSQL Specification](https://github.com/jirutka/rsql-parser) (for filtering syntax) - [RFC 7807 - Problem Details](https://tools.ietf.org/html/rfc7807) (error format) - [E.164 Phone Number Format](https://en.wikipedia.org/wiki/E.164) (international phone number standard) **Service Status**: - [8x8 Status Page](https://status.8x8.com) **Support**: - Admin Console: User profile menu → Contact Support **When Contacting Support**: See [Administration API Essentials - Support Resources](./suite-common.mdx#support-resources) for required information. --- **API Version**: 1.0 | **Last Updated**: January 15, 2026 | **Part of**: [Administration API Suite](./suite-common.mdx) | **Feedback**: Submit feedback via Admin Console --- ## Ring Group Management API Guide **API Version**: 1.0 | **Last Updated**: July 1, 2026 | **Part of**: [Administration API Suite](./suite-common.mdx) import TabbedExternalCodeSample from '@site/docusaurus/components/TabbedExternalCodeSample'; export const LANGUAGES = [ { ext: 'py', label: 'Python', syntax: 'python' }, { ext: 'js', label: 'Node.js', syntax: 'javascript' }, { ext: 'sh', label: 'cURL', syntax: 'bash' } ]; :::warning BETA - Limited Access **These Administration APIs are currently in Beta testing.** API keys cannot be generated in Admin Console at this point. Only Beta program participants may use these APIs. ::: ## Table of Contents 1. [Overview](#overview) 2. [Prerequisites & Authentication](#prerequisites--authentication) 3. [Getting Started](#getting-started) 4. [Core Concepts](#core-concepts) 5. [Use Cases](#use-cases) 6. [API Reference](#api-reference) 7. [Business Rules](#business-rules) 8. [API-Specific Error Scenarios](#api-specific-error-scenarios) 9. [API-Specific Troubleshooting](#api-specific-troubleshooting) 10. [Additional Resources](#additional-resources) --- ## Overview The Ring Group Management API enables customers and partners to programmatically manage ring groups across their 8x8 voice infrastructure. Ring groups distribute incoming calls among team members using configurable routing patterns, ensuring calls are efficiently handled by available staff. This API provides complete lifecycle management for ring groups, including: - Creating and configuring ring groups with custom routing patterns - Making targeted additions, updates, and removals to ring group membership without replacing the whole group - Configuring call forwarding rules and voicemail - Assigning phone numbers to ring groups - Searching and filtering ring groups with advanced query capabilities **Primary use cases**: - Automated provisioning of ring groups for new departments or teams - Bulk management of ring group members during organizational changes - Integration with HR systems for automatic member provisioning - Programmatic management of call forwarding and voicemail settings ### API Architecture - **RESTful Design**: Standard HTTP methods (GET, POST, PUT, DELETE) with JSON payloads - **Asynchronous Operations**: Create, update, and delete operations use async processing (see [API Essentials - Async Operations](./suite-common.mdx#asynchronous-operations)) - **RSQL Filtering**: Powerful query syntax for precise ring group searches (see [API Essentials - RSQL](./suite-common.mdx#filtering-with-rsql)) - **Scroll-Based Pagination**: Efficient pagination for large result sets (see [API Essentials - Pagination](./suite-common.mdx#pagination)) ### Reference View the complete [OpenAPI Specification](../../../docs_oas/administration/ringgroup-api-v1.yaml) for detailed endpoint documentation. --- ## Prerequisites & Authentication Before using the Ring Group Management API, review the **[Administration API Essentials](./suite-common.mdx)**, which covers: - **Prerequisites**: Account requirements, API credential acquisition, technical requirements - **Getting Started**: Step-by-step credential setup and testing - **Authentication**: API key authentication with `x-api-key` header - **API Versioning**: Version negotiation via the `Content-Type` header (on writes) and the `Accept` header (on reads) - **Common Patterns**: Asynchronous operations, pagination, filtering, sorting - **Error Handling**: RFC 7807 format and common error scenarios - **Rate Limiting**: Request limits and handling strategies - **Best Practices**: Performance, security, and data consistency - **Troubleshooting**: Common issues and debugging steps - **Support Resources**: Contact information and escalation procedures ### Ring Group Management API Specifics **Version header** for this API (see [API Versioning](./suite-common.mdx#api-versioning) for the full rule): - Requests with a payload (`POST`, `PUT`) carry the version in `Content-Type: application/vnd.ringgroups.v1+json` - Requests that return data (`GET`) carry it in `Accept: application/vnd.ringgroups.v1+json` **Required API Products** (when creating API key in Admin Console): - **UC & CC Admin Operations**: Required for ring group management operations **Ring-Group-Specific Prerequisites**: - Appropriate permissions to manage ring groups - Understanding of ring group concepts (ring patterns, forwarding rules) - Knowledge of user IDs for ring group members --- ## Getting Started This quickstart demonstrates how to create a new ring group and verify it was created successfully. You'll learn the basic request/response pattern and asynchronous operation tracking used throughout the API. **Prerequisite Knowledge**: This quickstart assumes you've reviewed the [Common Documentation](./suite-common.mdx) and understand: - API key authentication - Asynchronous operation patterns - How to poll operation status ### Create Your First Ring Group Ring groups require five essential properties: - `name`: A descriptive name for the ring group - `extensionNumber`: Internal extension number for routing - `ringPattern`: How calls are distributed (ROUND_ROBIN, SEQUENTIAL, or SIMULTANEOUS) - `ringTimeout`: Seconds each member's device rings before advancing - `site.id`: Valid Site ID from your organization (obtain from Admin Console → Sites or lookup via the [Site Management API](./site-management-api-guide)) Expected response for ring group creation: ```json { "operationId": "op_123456789", "status": "PENDING", "customerId": "0012J00042NkZQIQA3", "resourceType": "RING_GROUP", "operationType": "CREATE", "createdTime": "2025-01-01T01:02:03Z", "_links": { "self": { "href": "https://api.8x8.com/admin-provisioning/operations/op_123456789" } } } ``` For complete async operation handling, see [API Essentials - Asynchronous Operations](./suite-common.mdx#asynchronous-operations). --- ## Core Concepts Understanding these concepts specific to the Ring Group Management API will help you use it effectively. For general concepts (asynchronous operations, pagination, filtering, sorting, error handling), see [Administration API Essentials](./suite-common.mdx). ### Ring Patterns Ring groups support three distribution patterns: - **ROUND_ROBIN**: Starts with the member who was alerted last on the previous call, ensuring even distribution of incoming calls across all members - **SEQUENTIAL**: Always starts with the same member on each new call, proceeding through members in order based on their sequence number - **SIMULTANEOUS**: Alerts all members at the same time, with the first to answer receiving the call ### Ring Group Members Members are users assigned to receive calls distributed by the ring group. Each member has: - `userId` and `extensionId`: Unique identifiers - `sequenceNumber`: Determines order for ROUND_ROBIN and SEQUENTIAL patterns - `loggedIn`: Current login status (if allowLogInLogOut is enabled) - `voicemailAccessEnabled`: Permission to access ring group voicemail **Targeted membership changes**: To add, update, or remove specific members without resending the entire ring group, use the dedicated `POST /ring-groups/{ringGroupId}/update-members` endpoint. It applies an atomic delta — an `add`, `update`, and/or `remove` list in a single request — so you don't have to read the current member list, mutate it, and write it back. Within a single request, each member is identified by either its `extensionId` or `extensionNumber`, and the same identifier cannot appear in more than one list. See [Use Case 4: Manage Ring Group Members](#use-case-4-manage-ring-group-members). **How `sequenceNumber` is resolved**: For ROUND_ROBIN and SEQUENTIAL patterns, `sequenceNumber` sets each member's position in the alerting order. When you add or update members through `update-members`, the service treats the values you supply as *desired positions* and reconciles them against the existing members: - The `sequenceNumber` values in a single request must be **unique within that request** — if two members in the same request request the same position, the additions are rejected. - A supplied `sequenceNumber` **may collide with an existing member's** position. The new or updated member is placed at that desired position and the existing members at or below it shuffle **down** to make space. - If you **omit `sequenceNumber` on an `add`**, the new member is appended to the **end** of the sequence. - The final order is always a **contiguous sequence starting at 1 with no gaps**. Removing members closes the gap — the members below shuffle **up** so the numbering stays contiguous. ### Caller ID Configuration The `inboundCallerIdFormat` property controls what caller ID information members see: - `RGNAME_CALLERNUMBER`: Shows ring group name and caller's number - `CALLERNAME_CALLERNUMBER`: Shows caller's name and number - `RGNAME_RGEXTENSION`: Shows ring group name and extension - `RGNAME_DIALEDNUMBER`: Shows ring group name and dialed number ### Forwarding Rules Ring groups support forwarding rules that activate under specific conditions: - `UNCONDITIONALLY`: Always forward calls - `BUSY`: Forward when all members are busy - `NO_ANSWER`: Forward when no member answers within the timeout period - `OUTAGE`: Forward during system outages Destinations can be auto attendants, extension numbers, external numbers, voicemail, or call drop. --- ## Use Cases This section covers the most common real-world scenarios for using the Ring Group Management API. For common patterns like pagination, filtering, error handling, and rate limiting, see [Administration API Essentials](./suite-common.mdx). ### Use Case 1: Create Ring Group with Multiple Members When onboarding a new team or department, you need to create a ring group and add multiple members in a single workflow. This use case demonstrates: - Creating a ring group with optimal settings for team collaboration - Adding multiple members with appropriate sequence numbers - Configuring caller ID for professional presentation - Setting up voicemail for missed calls **Expected outcome**: Ring group created with all specified members, ready to receive calls with fair distribution. ### Use Case 2: Search and Filter Ring Groups When managing multiple ring groups across departments, you need to find specific groups using filters and handle paginated results. This use case demonstrates: - Using RSQL filter syntax with multiple operators (see [API Essentials - RSQL](./suite-common.mdx#filtering-with-rsql)) - Implementing cursor-based pagination with scrollId (see [API Essentials - Pagination](./suite-common.mdx#pagination)) - Sorting results by relevant fields (see [API Essentials - Sorting](./suite-common.mdx#sorting-results)) - Handling empty result sets **Expected outcome**: Filtered list of ring groups matching search criteria, efficiently paginated using scroll IDs and sorted as requested. ### Use Case 3: Update Ring Group Settings When business requirements change, you need to update ring group configuration such as ring patterns, timeouts, or caller ID settings. This use case demonstrates: - Retrieving current ring group configuration - Modifying specific settings while preserving others - Tracking update operation to completion (see [API Essentials - Async Operations](./suite-common.mdx#asynchronous-operations)) - Verifying changes were applied :::danger CRITICAL: Understanding PUT Semantics The Ring Group Management API does NOT support partial updates via PATCH. PUT operations require the COMPLETE ring group object. Be sure to familiarise yourself with the correct update pattern in the [Administration API Essentials - Understanding PUT Semantics](./suite-common.mdx#understanding-put-semantics) section to avoid unintended data loss. ::: **Expected outcome**: Ring group updated with new settings, all other configuration preserved. ### Use Case 4: Manage Ring Group Members When team composition changes, you need to add new members, remove departing members, or adjust member settings like sequence order or voicemail access. Use the dedicated `POST /ring-groups/{ringGroupId}/update-members` endpoint to apply these changes as a **targeted, atomic delta** — you send only the members that are changing, not the entire ring group. This is the recommended way to modify membership. It avoids the read-modify-write cycle of a full-object `PUT`, and the whole delta is applied atomically: either every change in the request succeeds or none do. This use case demonstrates: - Adding new members with `add` (sequence number, voicemail access) - Modifying existing members with `update` (for example, changing sequence order or voicemail access) - Removing departing members with `remove` - Combining all three in a single atomic request - Tracking the operation to completion (see [API Essentials - Async Operations](./suite-common.mdx#asynchronous-operations)) :::info Request body and identifiers The request body carries up to three optional lists — `add`, `update`, and `remove` — and **at least one must be non-empty**. Each list accepts up to 200 members. Identify each member by either `extensionId` or `extensionNumber`; the same identifier must not appear in more than one list. This endpoint uses its own version media type: `Content-Type: application/vnd.ringgroups.update-members.v1+json`. ::: A request body that adds one member, re-orders another, and removes a third looks like this: ```json { "add": [ { "extensionNumber": "1005", "sequenceNumber": 4, "voicemailAccessEnabled": true } ], "update": [ { "extensionNumber": "1002", "sequenceNumber": 1 } ], "remove": [ { "extensionNumber": "1003" } ] } ``` Like the other write operations, the endpoint responds `202 Accepted` with an Operation resource (`operationType: UPDATE_MEMBERS`) that you poll to completion: ```json { "operationId": "op_123456789", "status": "PENDING", "customerId": "0012J00042NkZQIQA3", "resourceType": "RING_GROUP", "resourceId": "aeP9pOoDRbq8_KKiwtsXhQ", "operationType": "UPDATE_MEMBERS", "createdTime": "2025-01-01T01:02:03Z", "_links": { "self": { "href": "https://api.8x8.com/admin-provisioning/operations/op_123456789" }, "resource": { "href": "https://api.8x8.com/admin-provisioning/ring-groups/aeP9pOoDRbq8_KKiwtsXhQ" } } } ``` **Expected outcome**: The specified members are added, updated, and/or removed atomically, and the rest of the ring group's membership and configuration is left untouched. :::note Replacing the entire member list If you genuinely need to replace the whole member list (or change other ring group settings at the same time), you can still send the complete `members` array via `PUT /ring-groups/{ringGroupId}` — see [Use Case 3: Update Ring Group Settings](#use-case-3-update-ring-group-settings) and the PUT-semantics guidance. For everyday membership changes, prefer `update-members`. ::: ### Use Case 5: Configure Forwarding Rules When ring groups need to handle overflow or after-hours calls, you must configure forwarding rules with appropriate conditions and destinations. This use case demonstrates: - Setting up NO_ANSWER forwarding to voicemail - Configuring BUSY forwarding to another extension - Setting up OUTAGE forwarding to external number - Enabling and disabling rules **Expected outcome**: Ring group with multiple forwarding rules handling various call scenarios. ### Use Case 6: Delete Ring Group When a ring group is no longer needed due to departmental changes or organizational restructuring, you must safely delete it and verify removal. This use case demonstrates: - Checking ring group exists before deletion - Initiating delete operation - Polling operation status (see [API Essentials - Async Operations](./suite-common.mdx#asynchronous-operations)) - Handling deletion errors - Verifying ring group no longer exists **Expected outcome**: Ring group permanently removed, confirmed by 404 response on retrieval. --- ## API Reference ### Base URL `https://api.8x8.com/admin-provisioning` ### Key Endpoints | Method | Endpoint | Purpose | |--------|----------|---------| | GET | `/ring-groups` | Search ring groups with filtering and pagination | | POST | `/ring-groups` | Create new ring group (async operation) | | GET | `/ring-groups/{ringGroupId}` | Retrieve specific ring group by ID | | PUT | `/ring-groups/{ringGroupId}` | Update ring group, replacing the full object (async operation) | | POST | `/ring-groups/{ringGroupId}/update-members` | Apply a targeted add/update/remove delta to ring group members (async operation) | | DELETE | `/ring-groups/{ringGroupId}` | Delete ring group (async operation) | ### Required Headers See [API Essentials - Common Request Patterns](./suite-common.mdx#common-request-patterns) for complete header requirements. For Ring Group Management API specifically: ```http x-api-key: your-api-key-here Content-Type: application/vnd.ringgroups.v1+json (on POST /ring-groups and PUT — carries the version) Content-Type: application/vnd.ringgroups.update-members.v1+json (on POST /ring-groups/{ringGroupId}/update-members only) Accept: application/vnd.ringgroups.v1+json (on GET) ``` The `POST /ring-groups/{ringGroupId}/update-members` endpoint uses its own request media type (see below). ### Version Media Type The version is carried in the `Content-Type` header on requests with a payload (`POST`, `PUT`) and in the `Accept` header on requests that return data (`GET`): ```text application/vnd.ringgroups.v1+json ``` **Exception — `update-members`**: The `POST /ring-groups/{ringGroupId}/update-members` endpoint uses a distinct request media type for its delta payload: ```text application/vnd.ringgroups.update-members.v1+json ``` Like the other async write operations, it returns an Operation resource (`application/vnd.operations.v1+json`). ### Important Parameters **Search/List Parameters**: - `pageSize`: Number of items per page (default: 100, min: 1, max: 1000) - see [API Essentials - Pagination](./suite-common.mdx#pagination) - `scrollId`: Cursor for pagination (obtained from `pagination.nextScrollId` in previous response) - `filter`: RSQL filter expression (max 2000 characters) - see [API Essentials - RSQL](./suite-common.mdx#filtering-with-rsql) - `sort`: Sort expression (max 200 characters) - see [API Essentials - Sorting](./suite-common.mdx#sorting-results) **Ring Group Properties**: - `name`: Ring group name (2-64 characters, required) - `extensionNumber`: Internal extension (required) - `ringPattern`: ROUND_ROBIN, SEQUENTIAL, or SIMULTANEOUS (required) - `ringTimeout`: Ring duration in seconds (required) - `inboundCallerIdFormat`: Caller ID display format (required) - `allowLogInLogOut`: Allow members to log in/out - `numberOfCycles`: Cycles through members before forwarding (ROUND_ROBIN/SEQUENTIAL only) - `voicemailSettings.accessPin`: Voicemail PIN (6-15 characters) For complete parameter details, see the [OpenAPI Specification](../../../docs_oas/administration/ringgroup-api-v1.yaml). ### HTTP Status Codes See [API Essentials - Error Handling](./suite-common.mdx#error-handling) for complete status code descriptions. **Success Codes**: - `200 OK`: Successful GET request - `202 Accepted`: Asynchronous operation initiated (POST, PUT, DELETE) **Error Codes**: - `400 Bad Request`: Invalid request parameters, validation errors, or malformed request body - `401 Unauthorized`: Missing or invalid API key authentication - `403 Forbidden`: Insufficient permissions or customer ID mismatch - `404 Not Found`: Ring group or related resource not found - `409 Conflict`: Ring group with same extension already exists - `424 Failed Dependency`: Downstream service failure or unavailable - `429 Too Many Requests`: Rate limit exceeded (see [API Essentials - Rate Limiting](./suite-common.mdx#rate-limiting)) - `500 Internal Server Error`: Unexpected server error occurred --- ## Business Rules This section documents important constraints and special behaviors specific to ring group management operations. ### Member Limits **Recommended Maximum**: 20 members per ring group. While the API does not enforce a strict limit on ring group membership, exceeding 20 members may cause stability issues: - **Call Distribution Delays**: Large member counts can slow call routing decisions - **Simultaneous Ring Impact**: SIMULTANEOUS ring pattern performance degrades significantly with many members - **System Resource Usage**: Each member requires system resources for call state tracking **Best Practices**: - Keep ring groups under 20 members for optimal performance - For larger teams, consider using multiple ring groups with overflow forwarding - Use call queues instead of ring groups for high-volume scenarios with many agents --- ## API-Specific Error Scenarios For common error handling patterns, see [Administration API Essentials - Error Handling](./suite-common.mdx#error-handling). This section covers error scenarios specific to the Ring Group Management API. ### Validation Errors (400) **Ring group name must be 2-64 characters**: ```json { "status": 400, "title": "Validation Error", "errors": [ { "field": "name", "code": "VALIDATION_ERROR", "message": "Ring group name must be between 2 and 64 characters" } ] } ``` **Required fields validation**: - `name`, `extensionNumber`, `ringPattern`, `ringTimeout`, `inboundCallerIdFormat` must be present - Ring timeout must be positive integer - Number of cycles only applies to ROUND_ROBIN and SEQUENTIAL patterns - Voicemail PIN must be 6-15 characters if provided **RSQL filter validation**: - Filter expression must be valid syntax (max 2000 characters) - See [API Essentials - RSQL](./suite-common.mdx#filtering-with-rsql) for syntax rules **Sort expression validation**: - Sort expression must be valid (max 200 characters) - See [API Essentials - Sorting](./suite-common.mdx#sorting-results) for syntax rules ### Conflict Errors (409) **Duplicate extension number**: ```json { "status": 409, "title": "Conflict", "detail": "Extension number already in use", "errors": [ { "field": "extensionNumber", "code": "CONFLICT", "message": "Extension 1001 is already assigned to another ring group or user" } ] } ``` **Resolution**: Search for existing ring groups with that extension, choose different extension number. ### Resource Not Found (404) **Ring group does not exist**: ```json { "status": 404, "title": "Not Found", "detail": "Ring group not found", "errors": [ { "code": "NOT_FOUND", "message": "Ring group with ID abc123 does not exist" } ] } ``` **Causes**: - Ring group ID is incorrect - Ring group was deleted - Related resource not found (e.g., site for ring group creation) **Resolution**: Verify resource ID is correct, check if resource exists in correct customer account. ### Membership Update Errors (`update-members`) These errors are specific to `POST /ring-groups/{ringGroupId}/update-members`. The `400` cases below are synchronous request-validation failures — the payload is rejected before any work is done. The `409` signals a conflict detected while applying the delta (for example, a concurrent modification of the ring group or a conflict passed through from the downstream voice configuration service). **Empty delta (400)** — the request must change at least one member: ```json { "status": 400, "title": "Invalid update-members request", "errors": [ { "code": "BAD_REQUEST", "message": "At least one of 'add', 'update', or 'remove' must be non-empty" } ] } ``` **Resolution**: Include at least one member in `add`, `update`, or `remove`. **Duplicate identifier across lists (400)** — the same member appears in more than one list: ```json { "status": 400, "title": "Invalid update-members request", "errors": [ { "code": "CONFLICTING_QUERY_PARAMETER", "field": "remove[0].extensionId", "message": "Identifier 'ext-1' appears in more than one array" } ] } ``` **Resolution**: Ensure each `extensionId` / `extensionNumber` appears in only one of `add`, `update`, or `remove`. **Conflict while applying the delta (409)** — a conflict was detected during processing (such as a concurrent modification or a downstream conflict): ```json { "status": 409, "title": "Conflicting update-members request", "errors": [ { "code": "CONFLICT", "message": "The ring group was modified concurrently; retry with the current member list" } ] } ``` **Resolution**: Re-fetch the ring group to get its current membership, rebuild your `add`/`update`/`remove` delta from that state, and resubmit. Note that some membership validations (for example, whether a member's extension actually exists, or whether the resulting member count is within limits) are performed asynchronously and surface via the operation's `error` field on a terminal status rather than in the initial `202` response — see [Operation Failures](#operation-failures). ### Operation Failures Poll operation status to get failure details. Common causes: - Invalid member IDs (users don't exist) - Duplicate extension numbers - Invalid phone number formats (must be E.164) - Site or PBX configuration issues See [API Essentials - Async Operations](./suite-common.mdx#asynchronous-operations) for handling operation errors. --- ## API-Specific Troubleshooting For common troubleshooting guidance, see [Administration API Essentials - Troubleshooting](./suite-common.mdx#troubleshooting). This section covers troubleshooting issues specific to the Ring Group Management API. ### Ring Group Not Receiving Calls **Problem**: Ring group created but not receiving calls **Debugging Steps**: 1. Verify phone numbers are assigned to ring group 2. Check forwarding rules aren't misconfigured (unconditional forward active) 3. Verify members are logged in (if `allowLogInLogOut` is true) 4. Check ring group status and configuration 5. Test with direct call to extension number **Resolution**: - Assign phone numbers via Admin Console or related API - Review and correct forwarding rules - Ensure at least one member is logged in and available - Verify extension number is correctly configured ### Members Not Showing in Ring Group **Problem**: Members not appearing in GET response after update **Debugging Steps**: 1. Check the update operation status (should be COMPLETED, not FAILED) 2. If you used `update-members`, confirm each member was placed in the intended `add`/`update`/`remove` list 3. If you used a full-object `PUT`, verify members were included in the complete `members` array 4. Ensure member `extensionId` / `extensionNumber` values are valid and the users exist 5. Retrieve the ring group to see the actual member list 6. Check the operation's `error` field for asynchronous validation failures **Resolution**: - For targeted changes, use `POST /ring-groups/{ringGroupId}/update-members` and send only the members that are changing - When using a full-object `PUT`, always include the complete `members` array (omitted members are removed) - Verify all member identifiers exist before submitting - Check operation error details for specific member issues ### Extension Number Conflicts **Problem**: 409 Conflict when creating ring group **Cause**: Extension number already in use by another ring group or user **Debugging Steps**: 1. Search for existing ring groups: `GET /ring-groups?filter=extensionNumber=={number}` 2. Check if extension assigned to user in Admin Console 3. Choose different available extension number **Resolution**: Use unique extension number within your organization's extension plan. ### Operation Stuck or Failed See [API Essentials - Troubleshooting - Async Operation Problems](./suite-common.mdx#async-operation-problems) for general async operation troubleshooting. **Ring-Group-Specific Causes**: - Invalid member IDs in members array - Invalid phone number formats - System constraint violations (e.g., maximum members exceeded) - Ring group configuration conflicts **Resolution**: - Check operation `error` field for specific details - Validate all member IDs exist - Ensure phone numbers are in E.164 format - Contact support if issue persists with operation ID --- ## Additional Resources **API Documentation**: - [Administration API Essentials](./suite-common.mdx) - [OpenAPI Specification](/administration/ringgroup-api-v1.yaml) - [8x8 Admin Console](https://admin.8x8.com) **Technical References**: - [RSQL Specification](https://github.com/jirutka/rsql-parser) (for filtering syntax) - [RFC 7807 - Problem Details](https://tools.ietf.org/html/rfc7807) (error format) - [E.164 Phone Number Format](https://en.wikipedia.org/wiki/E.164) **Service Status**: - [8x8 Status Page](https://status.8x8.com) **Support**: - Admin Console: User profile menu → Contact Support **When Contacting Support**: See [API Essentials - Support Resources](./suite-common.mdx#support-resources) for required information. --- **API Version**: 1.0 | **Last Updated**: July 1, 2026 | **Part of**: [Administration API Suite](./suite-common.mdx) | **Feedback**: Submit feedback via Admin Console --- ## Contact Search Customers who need to find specific contact information in our Contact Database can utilize the following endpoint. This is particularly useful for locating contacts based on criteria like name, organization, or other attributes. **Endpoint for Contact Search**: `https://api.8x8.com/directory-contacts/api/v3/contacts` ## 1. Obtain API Key for Contact Search Product To use the Contact Search endpoint, you must obtain a **Contact Search API Key**. This key is specifically used for all GET requests to search and retrieve contact information without making modifications. [How to get API Keys](/analytics/docs/how-to-get-api-keys) ## 2. Prepare Search Request Construct your search query using the supported query parameters to filter and sort the contact database. ### Parameters **Method: GET** #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | x-api-key | ✓ | Pass the API key returned from Admin Console for Contact App product | romc_MmFmMTI3sowe | #### Query Parameters | Name | Description` | Example | | --- | --- | --- | | details | Details needed for contact***Available values*** : TAG, ADDRESS, PHONE, EMAIL, EXTENSION For an in-depth explanation of each parameter and the overall structure, please refer to our [Contact Object Structure Guide](/administration/docs/contact-object-structure-guide) | PHONE,ADDRESS | | filter | Filtering capabilities by using Feed Item Query Language (FIQL) filter expressions | firstName==John | | filterByDisplayFlag | Flag to filter results by display criteria | true | #### Query Parameters Description **Filtering** [Contact Object Structure Guide](/administration/docs/contact-object-structure-guide) > 📘 **Filtering** > > This API is designed with filtering capabilities by using Feed Item Query Language (FIQL) filter expressions. > > FIQL introduces simple and composite operators which can be used to build basic and complex queries. > > If the filter is not specified, All objects are returned. > > ***URL encoding notes (FIQL in query params)*** > > When you pass FIQL in the filter query parameter, reserved characters are URL-encoded by clients/tools: > > * %3D = =, %3D%3D = == > * %27 = ', %3D%3D%27 = ==’ > * %2C = , > * %28 = ( > * %29 = ) > * %3B = ; > * %40 = @ > > Most clients encode automatically. > > ***FIQL Operators***: > > **Equality & Inequality** > > * `firstName==John` → Contacts with first name 'John'. > * `firstName!=John` → Contacts without the first name 'John'. > > **Comparisons** > > * `createdTimestamp>25 or createdTimestamp=gt=25` → Contacts created after timestamp 25. > * `createdTimestamp>=25 or createdTimestamp=ge=25` → Contacts created at or after timestamp 25. > * `createdTimestamp<25 or createdTimestamp=lt=25` → Contacts created before timestamp 25. > * `createdTimestamp<=25 or createdTimestamp=le=25` → Contacts created at or before timestamp 25. > > **Inclusion & Exclusion** > > * `pbxId=in=(US1,US2)` → Contacts with pbxId as 'US1' or 'US2'. > * `pbxId=out=(US1)` → Contacts excluding those with pbxId as 'US1'. > > **Logical Operators** > > * `firstName==John;lastName==Doe` → Contacts with first name 'John' AND last name 'Doe'. > * `firstName==John,lastName==Doe` → Contacts with first name 'John' OR last name 'Doe'. > > **Wildcards** > > * `firstName==Jo*` → Contacts with first names starting with 'Jo'. > * `firstName!=*ohn` → Contacts with first names not ending with 'ohn'. > > The primary sub-objects you can query are: > > * **tags**: Accessible using fields such as tag.id, tag.name, etc. > * **addresses**: Accessible using fields such as address.streetName , address.city , etc. > * **phones**: Accessible using fields such as phone.phone, phone.purposeType , etc. > * **emails**: Accessible using fields such as email.email , email.purposeType , email.primary, etc > * **extensions**: Accessible using fields such as extension.extension, extension.pbxName, extension.pbxId, etc > #### Pagination Parameters | Name | Description | Type | Example | | --- | --- | --- | --- | | scrollId | ID for scrolling through paginated results | String | WyI3YzA4YW0M2RjYTg2YWRiMzg2MDBkNTZhZCJd | | size | The size of the page to retrieve | Integer | 10 | | sort | Sorting criteria for the search results | String | name, ASC | | useScrollId | Indicates if the scroll ID should be used | Boolean | true | ### Pagination #### Keyset pagination This pagination method implies searching contacts within a key range(keyset) and the usage of this method requires additional parameters: * `useScrollId=true` * `scrollId=WyI3YzA4YW0M2RjYTg2YWRiMzg2MDBkNTZhZCJd` * Received in server response, for first request * You just need to pass it back as request parameter for the next request (a different value will be received for each request). This is empty for the first request. * `size=10&sort=id,ASC` * It is essential for sorting fields to contain "id" field * Additionally, you can also add sorting by other fields - ex "name" ## 3. Example Usage ### Without Pagination #### Request ```bash curl --location 'https://api.8x8.com/directory-contacts/api/v3/contacts?details=TAG%2CADDRESS%2CPHONE%2CEMAIL%2CEXTENSION&filter=contactType%3D%3D%27corporate%27%3BjobTitle%3D%3D%27Agent%27%3Bemail.email%3D%3Demail.changed%408x8.com' \ --header 'x-api-key: YOUR_CONTACT_SEARCH_API_KEY' ``` > 📘 **Request description** > > The request queries for contacts that are tagged as corporate and have the job title Agent, as well as a specific email [email.changed@8x8.com](mailto:email.changed@8x8.com). It asks for multiple details to be returned for each contact: TAG, ADDRESS, PHONE, EMAIL, and EXTENSION. > > #### Response ```json { "data": [ { "id": "2h_JNsaISleZWA36GRh3YQ", "assignedUserId": "2h_JNsaISleZWA36GRh3YQ", "branchId": "ovYzzgfDSDqolA3RbhWjbw", "branchName": "Tech Support Division", "companyName": "8x8 Inc.", "contactType": "corporate", "createdTimestamp": 1621341000000, "customerId": "0016C00000VM1BeQAL", "department": "Support", "displayWhenNoExtension": true, "firstName": "James", "hideInAA": false, "jobTitle": "Agent", "lastName": "Miller", "locale": "en_US", "location": "San Jose, CA", "middleName": "Edward", "name": "James Edward Miller", "nickName": "Jim", "pbxId": "bhjLT03CTJuVwgAy9y3DOQ", "pbxName": "qmsarealenv1", "pictureHash": "a046a853f0e131f18001d1174d8588ff76172a1350ba5ff1ba081caa470f6e1e", "timeZone": "America/Los_Angeles", "updatedTimestamp": 1678182231575, "addresses": [ { "id": 23071, "apartmentNumber": null, "city": "San Jose", "country": "United States", "county": "Santa Clara", "notes": "8x8 Headquarters", "postalCode": "95131", "primary": true, "purposeType": "BUSINESS", "state": "California", "streetName": "1st St", "streetNumber": "675" } ], "emails": [ { "id": 86533, "email": "user@example.com", "primary": true, "purposeType": "WORK" } ], "extensions": [ { "id": 52400, "branchId": "ovYzzgfDSDqolA3RbhWjbw", "branchName": "ContactSite", "contactId": "2h_JNsaISleZWA36GRh3YQ", "displayInDirectory": true, "extension": "60000001", "extensionType": "CC", "fqExtension": "1460000001", "pbxId": "bhjLT03CTJuVwgAy9y3DOQ", "pbxName": "qmsarealenv1", "subscriptionId": "aK60JcBERnKIya9Rt7BCxA", "subscriptionType": "UE" } ], "phones": [ { "id": 48073, "phone": "04029511367", "primary": false, "purposeType": "HOME", "source": "EXTERNAL" }, { "id": 48074, "phone": "0756124412", "primary": false, "purposeType": "WORK", "source": "EXTERNAL" } ], "tags": [ { "id": 25672, "name": "customField2", "value": "value2" }, { "id": 25673, "name": "customField1", "value": "value_updated" }, { "id": 25674, "name": "customField3", "value": "value3" } ] } ], "meta": { "hasMore": false, "scrollId": null } } ``` ### With Pagination #### Initial Request When you first request a paginated response, you don't have a scrollId yet. The initial request is sent without it: ```text GET https://api.8x8.com/directoryContacts/api/v3/contacts?size=10&sort=id,ASC&useScrollId=true Authorization: Bearer {access_token} ``` #### Initial Response ```json { "data": [ // Data not shown here as it's available in the previous example ], "meta": { "hasMore": true, "scrollId": "WyI2ODA1MDUyZDc1ZjY0N2E5OTQxYzdiYTJjNDU5ODc5OSJd" } } ``` > 📘 **Note** > > The initial response includes a scrollId, which is necessary for subsequent paginated requests. This ID ensures that the subsequent requests fetch the next set of results in the sequence. Remember to use page=0 in conjunction with scrollId for keyset pagination. > > #### Subsequent Request with `scrollId` For the next set of results, you'll use the scrollId provided in the initial response: ```text GET https://api.8x8.com/directoryContacts/api/v3/contacts?page=0&size=10&sort=id,ASC&useScrollId=true&scrollId=WyI2ODA1MDUyZDc1ZjY0N2E5OTQxYzdiYTJjNDU5ODc5OSJd Authorization: Bearer {access_token} ``` #### Subsequent Response ```json { "data": [ // Data not shown here as it's available in the previous example ], "meta": { "hasMore": true, "scrollId": "WyI3YzA4YWM5ZjkxNTU0M2RjYTg2YWRiMzg2MDBkNTZhZCJd" } } ``` ## Rate Limiting The Contact Search and Contact Management APIs are limited to **60 requests per minute**, in a rolling window, across all the keys under your customer account. If this limit is exceeded then a **429 Too Many Requests** response code will be returned. --- ## Site Management API Guide import TabbedExternalCodeSample from '@site/docusaurus/components/TabbedExternalCodeSample'; # Site Management API Guide **API Version**: 1.0 | **Last Updated**: January 15, 2026 | **Part of**: [Administration API Suite](./suite-common.mdx) :::warning BETA - Limited Access **These Administration APIs are currently in Beta testing.** API keys cannot be generated in Admin Console at this point. Only Beta program participants may use these APIs. ::: ## Table of Contents 1. [Overview](#overview) 2. [Core Concepts](#core-concepts) 3. [Getting Started](#getting-started) 4. [Use Cases](#use-cases) 5. [API Reference](#api-reference) 6. [API-Specific Error Scenarios](#api-specific-error-scenarios) 7. [API-Specific Troubleshooting](#api-specific-troubleshooting) 8. [Additional Resources](#additional-resources) --- ## Overview The Site Management API enables programmatic management of sites (physical or logical locations) and their associated addresses within your 8x8 organization. Sites are fundamental organizational units in the 8x8 hierarchy that contain users and UC resources, providing shared telephony features and emergency service configuration. This guide covers two related APIs: - **Site API**: Create, retrieve, update, and delete sites - **Address API**: Create, retrieve, and delete addresses used by sites Sites represent physical or logical customer locations such as offices, clinics, or facilities. Each site must have an associated address, which is registered with in-country authorities for emergency services and inherited by users at that site. ### Suite Integration This API is part of the Administration API Suite, which shares common patterns for authentication, versioning, error handling, and more. See the [Administration API Suite Common Documentation](./suite-common.mdx) for details on: - [Authentication](./suite-common.mdx#authentication) - [API Versioning](./suite-common.mdx#api-versioning) - [Asynchronous Operations](./suite-common.mdx#asynchronous-operations) - [Pagination](./suite-common.mdx#pagination) - [RSQL Filtering](./suite-common.mdx#filtering-with-rsql) - [Error Handling](./suite-common.mdx#error-handling) - [Rate Limiting](./suite-common.mdx#rate-limiting) ### API-Specific Versioning The Site Management APIs are versioned with these media types, carried in the `Content-Type` header on requests with a payload (`POST`, `PUT`) and in the `Accept` header on requests that return data (`GET`): - **Site API**: `application/vnd.sites.v1+json` - **Address API**: `application/vnd.addresses.v1+json` The Site `DELETE` is asynchronous and returns an operation resource (versioned via `Accept`), whereas the Address `DELETE` is synchronous, returns no content, and is not versioned. See [API Versioning](./suite-common.mdx#api-versioning) in the common documentation for the full rule. ## Core Concepts ### Sites Sites represent physical or logical locations within your organization where users and devices are deployed. Sites are part of the organizational hierarchy and provide shared configuration for users at that location. **Organizational Hierarchy**: ```text Customer → PBX → Site → Users/Resources ``` **Key Site Properties**: - **Name**: Human-readable identifier for the site (e.g., "Headquarters", "San Jose Office") - **PBX**: Parent PBX that owns the site (cannot be changed after creation) - **Address**: Physical address registered for emergency services (can be updated) - **Locale**: Default language for system prompts and desk phone displays (e.g., `en-US`, `fr-FR`) - **Timezone**: Timezone for the site, inherited by users (can be overridden at user level) - **Extension Management**: `extensionLength` and `siteCode` properties for extension configuration **Shared UC Telephony Features**: Sites provide shared configuration for users and resources at that location: - **Caller ID Settings** (`callerIdSettings`): - Main number for the site - Whether to use main number as caller ID - Whether to share main number across users - Display name for caller ID - **External Calling Permissions** (`defaultDialPlanRuleset`): - `INTERNATIONAL`: Allow international, domestic, and emergency calls - `DOMESTIC`: Allow domestic and emergency calls only - `EMERGENCYONLY`: Allow emergency calls only - **Emergency Notifications**: Email addresses to notify when emergency calls are made from the site **Site Lifecycle**: - Sites must exist before users can be created - Users must be linked to exactly one site - Sites can be updated after creation (including address changes) - Sites can be deleted only if no users or resources are associated ### Addresses Addresses are physical locations registered with in-country authorities for emergency services. Addresses are required for site creation and are inherited by users as their emergency addresses. **Key Address Characteristics**: - **Immutable**: Cannot be updated after creation (only created or deleted) - **Idempotent Creation**: If an identical address already exists, the API returns the existing address ID instead of creating a duplicate - **Reference Tracking**: The GET endpoint includes an `addressUsage` object with categorized usage counts - **Deletion Rules**: Can only be deleted when all usage counts are zero - **Country Mandatory**: The only required field for address creation **Address Usage Categories**: When you retrieve an address, the `addressUsage` object shows how many times the address is referenced in each category: - `customer`: Used as customer address - `site`: Used by sites (this guide's focus) - `userPersonal`: Used as user personal addresses - `userExtension`: Used as user extension addresses - `trunk`: Used by trunk configurations - `operatorConnect`: Used by Operator Connect configurations - `license`: Used by license configurations **Address Fields**: Addresses support various fields for different address formats worldwide: - `streetNumber`, `streetName`, `streetNumberSuffix`, `streetNameSuffix` - `preDirectional`, `postDirectional` (e.g., "N" in "123 N Main St") - `secondaryLocation` (e.g., "Unit 203", "Suite 400") - `city`, `dependentCity` - `state`, `county` - `postal`, `zip4` - `country` (required) - `building`, `organization` The API automatically populates a `displayForm` field with a formatted representation of the address. ### Locale and Timezone **Locale** specifies the default language for: - System audio prompts (e.g., voicemail instructions) - Desk phone display language **Timezone** determines: - When time-based features activate (e.g., scheduled call routing) - Timestamp display in user interfaces Both can be set at the site level and overridden at the user level, allowing users in different timezones or speaking different languages to work within the same site. ## Getting Started ### Prerequisites Before using the Site Management API, ensure you have: 1. **API Products**: The following API products must be enabled for your API key: - **UC Site Admin**: Required for site management operations - **UC & CC Admin Operations**: Required for asynchronous operations 2. **API Authentication**: An API key with appropriate permissions. See [Authentication](./suite-common.mdx#authentication) in the common documentation. 3. **Existing PBX**: Sites must be created within an existing PBX. If you don't have a PBX, create one first through the Admin Console or appropriate API. ### Base URL All Site Management API endpoints use the base URL: ```text https://api.8x8.com/admin-provisioning ``` ### Quick Start: Create Your First Site Creating a site is a two-step process: first create an address, then create a site that references that address. #### Step 1: Create an Address Address creation is synchronous and returns the created address immediately. **Request Example**: ```http POST /addresses HTTP/1.1 Host: api.8x8.com/admin-provisioning x-api-key: your-api-key-here Accept: application/vnd.addresses.v1+json Content-Type: application/vnd.addresses.v1+json { "streetNumber": "7", "streetName": "34TH ST", "secondaryLocation": "613", "city": "New York", "state": "NY", "postal": "10001", "country": "US" } ``` **Response Example** (200 OK): ```json { "id": "b1c4944a-17a0-4b00-8d48-36001df07e22", "displayForm": "7 W 34TH ST, 613, New York NY, 10001", "streetNumber": "7", "preDirectional": "W", "streetName": "34TH ST", "secondaryLocation": "613", "city": "New York", "state": "NY", "postal": "10001", "country": "US", "createdTime": "2025-01-01T01:02:03Z", "origin": "ADMIN_API" } ``` **Note**: Save the `id` value (`b1c4944a-17a0-4b00-8d48-36001df07e22`) for use in site creation. :::info Address API Uses Synchronous Create Operations Unlike other Administration APIs, the Address API's `POST /addresses` endpoint returns `200 OK` with the created Address object immediately, **not** `202 Accepted` with an Operation object. This is the only create operation in the Administration API Suite that does not use asynchronous processing. Site creation still uses the standard async pattern. ::: #### Step 2: Create a Site Site creation is asynchronous. The API returns an Operation object that you poll to track progress. **Request Example**: ```http POST /sites HTTP/1.1 Host: api.8x8.com/admin-provisioning x-api-key: your-api-key-here Accept: application/vnd.sites.v1+json Content-Type: application/vnd.sites.v1+json { "name": "Headquarters", "pbxName": "pbx01", "locale": "en-US", "timezone": "America/Los_Angeles", "siteCode": "1345", "extensionLength": 4, "address": { "id": "b1c4944a-17a0-4b00-8d48-36001df07e22" } } ``` **Response Example** (202 Accepted): ```json { "operationId": "op_123456789", "status": "PENDING", "customerId": "0012J00042NkZQIQA3", "resourceType": "SITE", "resourceId": "0023OEZiR7qQ_EGb6xYjgg", "operationType": "CREATE", "createdTime": "2025-01-01T01:02:03Z" } ``` **Note**: Save the `operationId` to poll for completion status. See [Asynchronous Operations](./suite-common.mdx#asynchronous-operations) for polling guidance. #### Step 3: Poll the Operation Site creation is asynchronous. Poll the operation to check its status. **Request Example**: ```http GET /operations/op_123456789 HTTP/1.1 Host: api.8x8.com/admin-provisioning x-api-key: your-api-key-here Accept: application/vnd.operations.v1+json ``` **Response Example** (200 OK - Completed): ```json { "operationId": "op_123456789", "status": "COMPLETED", "customerId": "0012J00042NkZQIQA3", "resourceType": "SITE", "resourceId": "0023OEZiR7qQ_EGb6xYjgg", "operationType": "CREATE", "createdTime": "2025-01-01T01:02:03Z", "completedTime": "2025-01-01T01:02:05Z", "_links": { "self": { "href": "/admin-provisioning/operations/op_123456789" }, "resource": { "href": "/admin-provisioning/sites/0023OEZiR7qQ_EGb6xYjgg" } } } ``` **Note**: When `status` is `COMPLETED`, use the `resourceId` or `_links.resource.href` to retrieve the created site. See [Asynchronous Operations](./suite-common.mdx#asynchronous-operations) in the common documentation for details on operation polling patterns and status values. ## Use Cases ### Use Case 1: Create an Emergency-Ready Site Create a new site with a validated address registered for emergency services. **Scenario**: Your organization is opening a new office in San Francisco. You need to create a site with emergency address registration so that users at this location can make emergency calls. **Steps**: 1. Create an address with complete street, city, state information 2. Handle address validation errors if they occur 3. Create a site referencing the address ID 4. Poll the operation until the site creation completes 5. Retrieve the created site to verify configuration **Request Example (Step 1 - Create Address)**: ```http POST /addresses HTTP/1.1 Host: api.8x8.com/admin-provisioning x-api-key: your-api-key-here Accept: application/vnd.addresses.v1+json Content-Type: application/vnd.addresses.v1+json { "streetNumber": "100", "streetName": "Market St", "city": "San Francisco", "state": "CA", "postal": "94105", "country": "US" } ``` **Response Example (Step 1)** (200 OK): ```json { "id": "addr_sf_market_st", "displayForm": "100 Market St, San Francisco CA, 94105", "streetNumber": "100", "streetName": "Market St", "city": "San Francisco", "state": "CA", "postal": "94105", "country": "US", "createdTime": "2025-01-01T01:02:03Z", "origin": "ADMIN_API" } ``` **Request Example (Step 2 - Create Site)**: ```http POST /sites HTTP/1.1 Host: api.8x8.com/admin-provisioning x-api-key: your-api-key-here Accept: application/vnd.sites.v1+json Content-Type: application/vnd.sites.v1+json { "name": "San Francisco Office", "pbxName": "pbx01", "locale": "en-US", "timezone": "America/Los_Angeles", "siteCode": "SF01", "extensionLength": 4, "address": { "id": "addr_sf_market_st" }, "emergencyNotifications": [ { "type": "EMAIL", "values": ["security@company.com", "facilities@company.com"] } ] } ``` **Response Example (Step 2)** (202 Accepted): ```json { "operationId": "op_create_sf_site", "status": "PENDING", "resourceType": "SITE", "operationType": "CREATE" } ``` **Note**: Address validation uses external providers. If validation fails, see the Troubleshooting section for guidance on handling address validation errors. ### Use Case 2: Configure Site Telephony Features Update an existing site's caller ID settings and external calling permissions. **Scenario**: Your San Francisco office site needs updated telephony configuration. You want to set a main phone number for caller ID and restrict external calling to domestic calls only. **Steps**: 1. Retrieve the current site configuration 2. Update the site with new caller ID settings and dial plan ruleset 3. Poll the operation until the update completes 4. Verify the updated configuration :::danger CRITICAL: Understanding PUT Semantics The Site Management API does NOT support partial updates via PATCH. PUT operations require the COMPLETE site object. Be sure to familiarise yourself with the correct update pattern in the [Administration API Essentials - Understanding PUT Semantics](./suite-common.mdx#understanding-put-semantics) section to avoid unintended data loss. ::: **Request Example**: ```http PUT /sites/{siteId} HTTP/1.1 Host: api.8x8.com/admin-provisioning x-api-key: your-api-key-here Accept: application/vnd.sites.v1+json Content-Type: application/vnd.sites.v1+json { "name": "San Francisco Office", "pbxName": "pbx01", "locale": "en-US", "timezone": "America/Los_Angeles", "siteCode": "SF01", "extensionLength": 4, "address": { "id": "addr_sf_market_st" }, "callerIdSettings": { "mainNumber": "+14155551234", "setMainNumberAsCallerId": true, "shareMainNumberAsCallerId": false, "displayName": "SF Office" }, "defaultDialPlanRuleset": "DOMESTIC" } ``` **Response Example** (202 Accepted): ```json { "operationId": "op_update_sf_site", "status": "PENDING", "resourceType": "SITE", "operationType": "UPDATE", "createdTime": "2025-01-01T01:02:03Z" } ``` **Note**: The site update is asynchronous. Poll the operation to check when it completes. **Caller ID Options**: - `setMainNumberAsCallerId`: When `true`, sets the main number as the default caller ID for users at this site - `shareMainNumberAsCallerId`: When `true`, allows users at this site to use the main number as their caller ID - `displayName`: Display name shown for caller ID **External Calling Permissions**: - `INTERNATIONAL`: Allow international, domestic, and emergency calls - `DOMESTIC`: Allow domestic and emergency calls only (restricts international) - `EMERGENCYONLY`: Allow emergency calls only (restricts all other outbound calling) ### Use Case 3: Search Sites by Name List sites in a PBX with wildcard name filtering. **Scenario**: You need to find all sites in your organization whose names start with "Office". **Steps**: 1. Use the GET /sites endpoint with a filter parameter 2. Parse the paginated results 3. Handle pagination if there are many results **Request Example**: ```http GET /sites?filter=name==Office*&pageSize=100&pageNumber=0 HTTP/1.1 Host: api.8x8.com/admin-provisioning x-api-key: your-api-key-here Accept: application/vnd.sites.v1+json ``` **Response Example** (200 OK): ```json { "data": [ { "id": "site_sf", "name": "Office - San Francisco", "pbxName": "pbx01", "locale": "en-US", "timezone": "America/Los_Angeles", "siteCode": "SF01", "extensionLength": 4, "address": { "id": "addr_sf" } }, { "id": "site_ny", "name": "Office - New York", "pbxName": "pbx01", "locale": "en-US", "timezone": "America/New_York", "siteCode": "NY01", "extensionLength": 4, "address": { "id": "addr_ny" } } ], "pagination": { "pageSize": 100, "pageNumber": 0, "hasMore": false } } ``` **Note**: Site search uses limited RSQL support - see the API-Specific Patterns section below. :::info Site Search Has Limited RSQL Support The Site API has **limited RSQL filtering support** compared to other Administration APIs: - **Only the `name` field** can be filtered - **Only the `==` operator** is supported - **Wildcards** (`*`) can appear at the beginning or end of the value (e.g., `name==Office*`, `name==*Branch`, `name==*Office*`) - **No logical operations** (`AND`, `OR`) are supported This is different from the full RSQL filtering described in the [common documentation](./suite-common.mdx#filtering-with-rsql). **Valid Examples**: - `name==Headquarters` (exact match) - `name==Office*` (starts with "Office") - `name==*Branch` (ends with "Branch") - `name==*Office*` (contains "Office") **Invalid Examples**: - `filter=pbxName==pbx01` (unsupported field) - `filter=name!=Office` (unsupported operator) - `filter=name==Office*;locale==en-US` (logical operations not supported) ::: ### Use Case 4: Delete Site and Unused Address Delete a site and its associated address when the site is no longer needed. **Scenario**: Your organization is closing the San Francisco office. You need to delete the site and, if the address is not used elsewhere, delete the address as well. **Steps**: 1. Ensure no users or resources are assigned to the site 2. Delete the site 3. Poll the operation until the deletion completes 4. Check the address usage counts 5. If all usage counts are zero, delete the address **Request Example (Step 1 - Delete Site)**: ```http DELETE /sites/{siteId} HTTP/1.1 Host: api.8x8.com/admin-provisioning x-api-key: your-api-key-here Accept: application/vnd.sites.v1+json ``` **Response Example (Step 1)** (202 Accepted): ```json { "operationId": "op_delete_sf_site", "status": "PENDING", "resourceType": "SITE", "operationType": "DELETE", "createdTime": "2025-01-01T01:02:03Z" } ``` **Request Example (Step 2 - Check Address Usage)**: ```http GET /addresses/{addressId} HTTP/1.1 Host: api.8x8.com/admin-provisioning x-api-key: your-api-key-here Accept: application/vnd.addresses.v1+json ``` **Response Example (Step 2)** (200 OK): ```json { "id": "addr_sf_market_st", "displayForm": "100 Market St, San Francisco CA, 94105", "country": "US", "addressUsage": { "customer": 0, "site": 0, "userPersonal": 0, "userExtension": 0, "trunk": 0, "operatorConnect": 0, "license": 0 } } ``` **Request Example (Step 3 - Delete Address)**: ```http DELETE /addresses/{addressId} HTTP/1.1 Host: api.8x8.com/admin-provisioning x-api-key: your-api-key-here ``` The address `DELETE` is synchronous and returns no content (`204`), so it is **not versioned** — no version media type is required in `Content-Type` or `Accept`. **Response Example (Step 3)** (204 No Content): ```text (Empty response body) ``` **Note**: Site deletion will fail if any users or resources are still assigned to the site. Address deletion will fail if the address is still in use (any usage count > 0). ### Use Case 5: Check Address Reusability Check if an address can be deleted or is still in use by other resources. **Scenario**: You want to clean up unused addresses in your system. You need to identify which addresses can be safely deleted. **Steps**: 1. List all addresses 2. For each address, check its usage counts 3. Identify addresses with zero usage counts that can be deleted **Request Example**: ```http GET /addresses/{addressId} HTTP/1.1 Host: api.8x8.com/admin-provisioning x-api-key: your-api-key-here Accept: application/vnd.addresses.v1+json ``` **Response Example** (200 OK): ```json { "id": "addr_ny_shared", "displayForm": "7 W 34TH ST, 613, New York NY, 10001", "streetNumber": "7", "preDirectional": "W", "streetName": "34TH ST", "secondaryLocation": "613", "city": "New York", "state": "NY", "postal": "10001", "country": "US", "createdTime": "2025-01-01T01:02:03Z", "origin": "ADMIN_API", "addressUsage": { "customer": 1, "site": 2, "userPersonal": 0, "userExtension": 3, "trunk": 0, "operatorConnect": 0, "license": 0 } } ``` **Note**: This address is in use by 1 customer, 2 sites, and 3 user extensions. It cannot be deleted until all usage counts are zero. **Address Usage Categories**: - `customer`: Number of customer records using this address - `site`: Number of sites using this address (focus of this guide) - `userPersonal`: Number of user personal addresses - `userExtension`: Number of user extension addresses - `trunk`: Number of trunk configurations using this address - `operatorConnect`: Number of Operator Connect configurations - `license`: Number of license configurations An address can only be deleted when **all** usage counts are zero. ## API Reference ### Site API #### Create Site - **Endpoint**: `POST /sites` - **Async**: Yes (returns Operation object) - **Description**: Creates a new site within a PBX **Required Fields**: `name`, `pbxName`, `locale`, `timezone`, `address.id` **Key Parameters**: - `address.id`: ID of a previously created address - `locale`: Language/locale code (e.g., `en-US`, `fr-FR`, `de-DE`) - `timezone`: IANA timezone (e.g., `America/Los_Angeles`, `Europe/London`) - `siteCode`: Optional site code for extension management - `extensionLength`: Optional extension length (digits) #### Get Site - **Endpoint**: `GET /sites/{siteId}` - **Description**: Retrieves a specific site by ID **Response includes**: All site properties including caller ID settings, dial plan ruleset, and emergency notifications. #### Search Sites - **Endpoint**: `GET /sites` - **Query Parameters**: - `pageSize`: Number of items per page (default: 100) - `pageNumber`: Page number (0-indexed, default: 0) - `filter`: RSQL filter expression (limited to `name` field with `==` operator) - `sort`: Sort expression (e.g., `name`, `-name` for descending) **Filtering**: Limited to `name` field only. See "Site Search Has Limited RSQL Support" callout above. #### Update Site - **Endpoint**: `PUT /sites/{siteId}` - **Async**: Yes (returns Operation object) - **Description**: Updates an existing site **Updatable Fields**: All fields except `id` and `pbxName`. The PBX cannot be changed after site creation. **Note**: To update a site's address, provide a different `address.id` value. #### Delete Site - **Endpoint**: `DELETE /sites/{siteId}` - **Async**: Yes (returns Operation object) - **Description**: Deletes a site **Precondition**: The site must have no users or resources assigned. If users exist, the operation will fail with a `400 Bad Request` error. ### Address API #### Create Address - **Endpoint**: `POST /addresses` - **Async**: No (synchronous, returns Address object immediately) - **Description**: Creates a new address or returns existing address if identical **Required Fields**: `country` only **Idempotency**: If an address with identical field values already exists, the API returns the existing address ID instead of creating a duplicate. **Validation**: Uses external validation providers. Legitimate addresses may sometimes be rejected due to validation provider limitations. See Troubleshooting for guidance. #### Get Address - **Endpoint**: `GET /addresses/{addressId}` - **Description**: Retrieves a specific address by ID **Response includes**: All address fields plus `addressUsage` object with categorized usage counts. #### Search Addresses - **Endpoint**: `GET /addresses` - **Query Parameters**: - `pageSize`: Number of items per page (default: 100) - `scrollId`: Scroll identifier for pagination (see [Pagination](./suite-common.mdx#pagination)) - `filter`: RSQL filter expression (supports `country`, `state`, `displayForm` fields) - `sort`: Sort expression (e.g., `city`, `-city` for descending) **Pagination**: Uses scroll-based pagination. See [Pagination](./suite-common.mdx#pagination) in the common documentation. **Filtering**: Supports full RSQL syntax for `country`, `state`, and `displayForm` fields. #### Delete Address - **Endpoint**: `DELETE /addresses/{addressId}` - **Async**: No (synchronous, returns `204 No Content`) - **Description**: Deletes an address **Precondition**: All `addressUsage` counts must be zero. If the address is in use, the operation will fail with a `400 Bad Request` error. ## Common Patterns This API follows standard patterns covered in the **[Administration API Essentials](./suite-common.mdx)**: - **Authentication**: See [Common Docs - Authentication](./suite-common.mdx#authentication) - **Asynchronous Operations**: See [Common Docs - Async Operations](./suite-common.mdx#asynchronous-operations) - **Pagination**: See [Common Docs - Pagination](./suite-common.mdx#pagination) - **Filtering with RSQL**: See [Common Docs - Filtering](./suite-common.mdx#filtering-with-rsql) - **Sorting**: See [Common Docs - Sorting](./suite-common.mdx#sorting-results) - **Error Handling**: See [Common Docs - Error Handling](./suite-common.mdx#error-handling) - **Rate Limiting**: See [Common Docs - Rate Limiting](./suite-common.mdx#rate-limiting) ### API-Specific Pattern Notes **Mixed Synchronous/Asynchronous Operations**: Site create, update, and delete operations are asynchronous (requiring operation polling), while Address create and delete operations are synchronous (immediate response). **Limited RSQL for Site Search**: Site search has limited RSQL support compared to full RSQL capabilities available in Address search. See Use Case 3 for details on Site search limitations. ## API-Specific Error Scenarios ### Common Errors All Site Management APIs follow the RFC 7807 Problem Details standard. See [Error Handling](./suite-common.mdx#error-handling) in the common documentation for the standard error response format. ### API-Specific Error Scenarios #### Site API Errors **Site Creation Errors**: ```json { "status": 400, "title": "Bad Request", "errors": [ { "code": "VALIDATION_ERROR", "field": "address.id", "message": "Address not found" } ] } ``` **Common Site API Error Codes**: - `VALIDATION_ERROR`: Invalid field values (e.g., invalid locale, timezone, or address ID) - `NOT_FOUND`: Site or address not found - `BAD_REQUEST`: Cannot delete site that has users assigned - `CONFLICT`: Site code already in use **Site Deletion Errors**: ```json { "status": 400, "title": "Bad Request", "errors": [ { "code": "BAD_REQUEST", "message": "Branch with id 0023OEZiR7qQ_EGb6xYjgg can not be removed. It is used by at least one extension" } ] } ``` **Solution**: Move or delete all users and resources from the site before attempting deletion. **Site Search Filtering Errors**: ```json { "status": 400, "title": "Invalid filter format", "errors": [ { "code": "VALIDATION_ERROR", "message": "Invalid filter field: id. Only filtering by name is supported" } ] } ``` **Solution**: Use only `name` field with `==` operator. No logical operations allowed. #### Address API Errors **Address Validation Errors**: ```json { "status": 400, "title": "Multiple validation errors", "errors": [ { "code": "VALIDATION_ERROR", "field": "city", "message": "Validation provider returned a different address" }, { "code": "VALIDATION_ERROR", "field": "streetName", "message": "Validation provider returned a different address" } ] } ``` **Solution**: See "Address Validation Challenges" in the Troubleshooting section below. **Address Deletion Errors**: ```json { "status": 400, "title": "Address is currently in use and cannot be deleted", "errors": [ { "code": "BAD_REQUEST", "message": "Address is currently in use and cannot be deleted" } ] } ``` **Solution**: Use `GET /addresses/{addressId}` to check `addressUsage` counts. The address cannot be deleted until all usage counts are zero. **Country Code Errors**: ```json { "status": 400, "title": "Failed to create address", "errors": [ { "code": "VALIDATION_ERROR", "message": "Invalid country code" } ] } ``` **Solution**: Use valid ISO 3166-1 alpha-2 country codes (e.g., `US`, `GB`, `FR`, `DE`). ### Asynchronous Operation Errors When an asynchronous operation fails, the Operation object includes an error field. See [Asynchronous Operations Error Handling](./suite-common.mdx#error-handling) for details on checking operation errors. **Response Example** (200 OK - Operation Failed): ```json { "operationId": "op_failed_example", "status": "FAILED", "resourceType": "SITE", "operationType": "CREATE", "error": { "status": 400, "title": "Address not found", "errors": [ { "code": "VALIDATION_ERROR", "field": "address.id", "message": "Address with ID invalid_address_id does not exist" } ] } } ``` **Note**: When status is FAILED, check the error field for details. ## Best Practices ### Site Management Best Practices 1. **Create Addresses Before Sites**: Always create addresses first and use the returned address ID when creating sites. 2. **Validate Address Data**: Ensure address data is as complete and accurate as possible to maximize validation success rates. 3. **Use Idempotent Address Creation**: Take advantage of automatic address deduplication. You don't need to search for existing addresses before creating. 4. **Check Address Usage Before Deletion**: Always retrieve the address and check `addressUsage` counts before attempting deletion. 5. **Handle Validation Errors Gracefully**: Address validation can fail for legitimate addresses. Have a fallback plan (see Troubleshooting). 6. **Respect Site-User Dependencies**: Ensure no users are assigned to a site before attempting deletion. 7. **Use Appropriate Dial Plan Rulesets**: Set `defaultDialPlanRuleset` based on your organization's calling policies: - Use `DOMESTIC` to prevent accidental international calls - Use `EMERGENCYONLY` for kiosks or public areas where only emergency calling is needed 8. **Configure Emergency Notifications**: Always set `emergencyNotifications` so appropriate personnel are alerted when emergency calls are made. 9. **Choose Appropriate Locales**: Set site `locale` based on the primary language spoken at that location to ensure users hear prompts in their language. 10. **Use Site Codes Consistently**: If using `siteCode` for extension management, establish a consistent naming convention across your organization. ### API Usage Best Practices For general API best practices including rate limiting, error handling, and retry strategies, see [Best Practices](./suite-common.mdx#best-practices) in the common documentation. ## API-Specific Troubleshooting ### Address Validation Challenges Address validation uses external validation providers to ensure accuracy for emergency services. However, these providers sometimes incorrectly reject legitimate addresses. **Common Issues**: - Address exists but validation provider doesn't recognize it - Address format doesn't match provider's expectations - New addresses not yet in provider's database **Troubleshooting Steps**: 1. **Verify Address Fields**: Double-check that all address components are spelled correctly and use standard abbreviations. 2. **Try Different Field Mappings**: If validation fails, try putting address components in different fields. For example: - Move suite/unit number from `secondaryLocation` to `building` - Try with or without street suffix (e.g., "Street" vs "St") - Adjust which fields contain apartment/unit information 3. **Simplify the Address**: Remove optional fields and try with only the essential components (`streetNumber`, `streetName`, `city`, `state`, `postal`, `country`). 4. **Check for Typos**: Small typos in street names or city names can cause validation failures. 5. **Verify Country Code**: Ensure you're using the correct ISO 3166-1 alpha-2 country code (e.g., `US` not `USA`). 6. **Last Resort - Admin Console**: If the API validation continues to fail for a legitimate address, create the site manually through the Admin Console UI. The Admin Console may have alternate validation paths or manual override capabilities. **Example Validation Error**: ```json { "status": 400, "title": "Multiple validation errors", "errors": [ { "code": "VALIDATION_ERROR", "field": "city", "message": "Validation provider returned a different address" } ] } ``` **Resolution**: Try adjusting the address field values, or use the Admin Console as a workaround. ### Site Deletion Failures **Error**: "Branch with id X can not be removed. It is used by at least one extension" **Cause**: Users or resources are still assigned to the site. **Solution**: 1. Use the User Management API to list all users at the site 2. Move users to a different site or delete them 3. Check for other resources (ring groups, auto-attendants) assigned to the site 4. After all resources are removed, retry site deletion ### Site Search Not Finding Expected Results **Issue**: Your RSQL filter returns no results or an error. **Possible Causes**: 1. You're filtering by a field other than `name` (not supported) 2. You're using an operator other than `==` (not supported) 3. You're using logical operations (`AND`, `OR`) (not supported) 4. Wildcard is in the middle of the value (not supported) **Solution**: Use only `name==pattern` where pattern can have wildcards at the beginning or end only. ### Address Cannot Be Deleted **Error**: "Address is currently in use and cannot be deleted" **Cause**: The address has non-zero usage counts in one or more categories. **Solution**: 1. Use `GET /addresses/{addressId}` to retrieve the `addressUsage` object 2. Identify which categories have non-zero counts 3. Remove the address references from those resources (e.g., update sites to use different addresses) 4. Verify all usage counts are zero before retrying deletion ### Operation Stuck in PENDING Status **Issue**: A site create/update/delete operation remains in `PENDING` status for an extended period. **Solution**: See [Troubleshooting Asynchronous Operations](./suite-common.mdx#troubleshooting) in the common documentation for guidance on handling stuck operations. ### Rate Limit Exceeded **Error**: `429 Too Many Requests` **Solution**: See [Rate Limiting](./suite-common.mdx#rate-limiting) in the common documentation for rate limits and retry strategies. ### Downstream Service Failures **Error**: `424 Failed Dependency` **Cause**: A downstream internal service is unavailable or timing out. **Solution**: 1. Retry the request after a short delay (exponential backoff recommended) 2. If the problem persists, contact 8x8 support 3. Check the [8x8 Status Page](https://status.8x8.com) for known service issues ## Additional Resources **API Documentation**: - [Administration API Essentials](./suite-common.mdx) - [OpenAPI Specification](/administration/site-api-v1.yaml) - [8x8 Admin Console](https://admin.8x8.com) **Technical References**: - [RSQL Specification](https://github.com/jirutka/rsql-parser) (for filtering syntax) - [RFC 7807 - Problem Details](https://tools.ietf.org/html/rfc7807) (error format) - [ISO 3166-1 Country Codes](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2) (country codes) - [IANA Time Zone Database](https://www.iana.org/time-zones) (timezone identifiers) **Service Status**: - [8x8 Status Page](https://status.8x8.com) **Support**: - Admin Console: User profile menu → Contact Support **When Contacting Support**: See [API Essentials - Support Resources](./suite-common.mdx#support-resources) for required information. --- **API Version**: 1.0 | **Last Updated**: January 15, 2026 | **Part of**: [Administration API Suite](./suite-common.mdx) | **Feedback**: Submit feedback via Admin Console --- ## Administration API Essentials **Version**: 1.0 | **Last Updated**: January 16, 2026 | **Applies to**: All Administration API Suite APIs import TabbedExternalCodeSample from '@site/docusaurus/components/TabbedExternalCodeSample'; :::warning BETA - Limited Access **These Administration APIs are currently in Beta testing.** API keys cannot be generated in Admin Console at this point. Only Beta program participants may use these APIs. ::: ## Table of Contents 1. [Suite Overview](#suite-overview) 2. [Prerequisites](#prerequisites) 3. [Getting Started](#getting-started) 4. [Authentication](#authentication) 5. [API Versioning](#api-versioning) 6. [Common Request Patterns](#common-request-patterns) 7. [Asynchronous Operations](#asynchronous-operations) 8. [Pagination](#pagination) 9. [Filtering with RSQL](#filtering-with-rsql) 10. [Sorting Results](#sorting-results) 11. [Error Handling](#error-handling) 12. [Rate Limiting](#rate-limiting) 13. [Best Practices](#best-practices) 14. [Troubleshooting](#troubleshooting) 15. [Support Resources](#support-resources) --- ## Suite Overview The Administration API Suite provides programmatic access to 8x8 administrative functions, enabling automation of user management, organizational configuration, and telephony provisioning. All APIs in this suite share common authentication, patterns, and infrastructure for a consistent developer experience. ### APIs in This Suite - **User Management API**: Manage users, roles, and permissions across your 8x8 organization - **Ring Group Management API**: Configure ring groups and call distribution - **Phone Number Management API**: Discover and retrieve phone number inventory and availability - **Site Management API**: Manage organizational sites and physical locations - **Contact Management API**: Manage contacts and contact information ### When to Use Which API - **User Management API**: For creating, updating, and deleting individual user accounts and managing their profiles - **Ring Group Management API**: For setting up and managing team call distribution and ring group configurations - **Phone Number Management API**: For discovering available phone numbers and retrieving phone number metadata before assignment - **Site Management API**: For managing sites, addresses, and organizational structure - **Contact Management API**: For creating and managing contacts associated with your organization ### Common Characteristics All Administration APIs share: - API key authentication with `x-api-key` header - Base URL: `https://api.8x8.com/admin-provisioning` - Asynchronous operation patterns for mutations (CREATE, UPDATE, DELETE) - Scroll-based pagination for list operations - RSQL filtering syntax for precise searches - RFC 7807 Problem Details error format - Rate limiting and security best practices --- ## Prerequisites Before using any Administration API, ensure you have the following: ### Required Access **8x8 Account Requirements**: - Active 8x8 account with Admin Console access - API Administrator role or equivalent permissions - Access to the customer organization where you'll manage resources **Technical Requirements**: - HTTPS-capable client (Python, Node.js, or equivalent programming language) - Understanding of REST API principles - Familiarity with JSON data format ### Knowledge Prerequisites **8x8 Platform Understanding**: - Basic knowledge of 8x8's organizational hierarchy (Customer → PBX → Site → Resources) - Familiarity with 8x8 Admin Console navigation - Understanding of user roles and permissions within 8x8 **Technical Skills**: - Experience with REST API integration - Understanding of asynchronous operation patterns - Knowledge of RSQL query syntax (for filtering) - examples provided in this guide --- ## Getting Started ### Obtaining API Credentials API keys are generated and managed through the 8x8 Admin Console. Follow these steps to obtain your API credentials: 1. Log in to the 8x8 Admin Console at [https://admin.8x8.com](https://admin.8x8.com) 2. Open the main navigation menu and go to **API Keys** 3. Click **Create App** 4. Add the appropriate API products to your key based on which Administration APIs you need: - **UC & CC Admin Operations**: Required if your key will be used to make changes on any APIs with asynchronous operations - **UC & CC Number Admin**: Required for number management operations - **UC & CC User Admin**: Required for user management operations - **UC Ring Group Admin**: Required for ring group management operations - **UC Site Admin**: Required for site management operations 5. Copy and securely store your API key 6. Note your customer ID for reference **Security Note**: Treat API keys like passwords. Never commit them to source control, share them in unsecured channels, or log them in plain text. Store them in environment variables or secure vault systems (AWS Secrets Manager, Azure Key Vault, etc.). ### Testing Your Credentials Once you have your API key, test that it works correctly by making a simple authenticated request: A successful response (HTTP 200 or 202) confirms your credentials are valid and properly configured. --- ## Authentication All Administration APIs use API key authentication passed in the `x-api-key` request header. Every request must include this header with a valid API key. ### Authentication Method Authentication uses the `x-api-key` header: ```http x-api-key: your-api-key-here ``` This header must be included in every API request. Requests without valid authentication will receive a `401 Unauthorized` response. ### Base URL **Production**: `https://api.8x8.com/admin-provisioning` All Administration API endpoints are accessed through this base URL. --- ## API Versioning Administration APIs use the `Content-Type` and `Accept` headers for version negotiation. This approach allows the APIs to evolve while maintaining backward compatibility for existing integrations. ### Version Format Versions use **major version numbers only** (v1, v2, etc., not v1.4 or v1.2.3). Minor updates and patches are deployed transparently without requiring version changes. ### Specifying Version The header used to specify the version depends on the nature of the endpoint: | Which endpoints? | Header | | :---- | :---- | | Any that take a request payload (e.g. `POST`, `PUT`) | `Content-Type` | | Any that return a response payload (e.g. `GET`) — optional where `Content-Type` is also provided, e.g. on `POST` | `Accept` | | Any that have neither a request nor a response payload (e.g. a synchronous `DELETE`) — these endpoints are not versioned | none | In all cases, regardless of whether you use the `Content-Type` or `Accept` header, the value follows this pattern: ```http application/vnd.{resource}.v{major}+json ``` Where: - `{resource}` is the API resource type (e.g., `users`, `ringgroups`) - `{major}` is the major version number (e.g., `1`, `2`) **Examples:** **User Management API** — creating a user (request payload, so `Content-Type`): ```http Content-Type: application/vnd.users.v1+json ``` **Ring Group Management API** — listing ring groups (response payload, so `Accept`): ```http Accept: application/vnd.ringgroups.v1+json ``` For other Administration APIs, check the specific API guide for its exact version header format. ### Backward Compatibility - Breaking changes only occur in major version increments - Non-breaking enhancements (see [Change Policy](./api-change-policy.md#change-categories)) and bug fixes are deployed to the current major version - Deprecated major versions are maintained for extended periods (typically 12 months after the next version release) --- ## Common Request Patterns ### Required Headers Every API request to Administration APIs must include your API key: ```http x-api-key: your-api-key-here ``` The API version is carried in a media-type header chosen by the operation (see [API Versioning](#api-versioning)): - **Requests with a payload** (`POST`, `PUT`) carry the version in `Content-Type`: ```http Content-Type: application/vnd.{resource}.v{major}+json ``` - **Requests that only retrieve data** (`GET`) carry the version in `Accept`: ```http Accept: application/vnd.{resource}.v{major}+json ``` ### Example Request ```http GET https://api.8x8.com/admin-provisioning/users?pageSize=10 x-api-key: your-api-key-here Accept: application/vnd.users.v1+json ``` ```http POST https://api.8x8.com/admin-provisioning/users x-api-key: your-api-key-here Content-Type: application/vnd.users.v1+json { "basicInfo": { "userName": "jane.smith@example.com", "firstName": "Jane", "lastName": "Smith", "primaryEmail": "jane.smith@example.com" } } ``` --- ## Asynchronous Operations All mutation operations (CREATE, UPDATE, DELETE) in Administration APIs use asynchronous processing to prevent timeouts, enable bulk operations, and provide operation tracking. ### Operation Flow 1. **Submit Request**: Client sends POST, PUT, or DELETE request 2. **Receive Acknowledgment**: API immediately returns `202 Accepted` with operation object 3. **Poll Status**: Client periodically checks operation status via GET `/operations/{operationId}` 4. **Check Status**: Operation progresses through states: `PENDING` → `IN_PROGRESS` → `COMPLETED` or `FAILED` 5. **Retrieve Resource**: Once `COMPLETED`, use `resourceId` to get created/updated resource ### Operation Status Values - **PENDING**: Operation queued but not yet started - **IN_PROGRESS**: Operation currently processing - **COMPLETED**: Operation finished successfully - **FAILED**: Operation encountered an error - **UNKNOWN**: Status cannot be determined (rare, contact support if seen) ### Operation Object Structure When you submit a mutation request, you receive an operation object: ```json { "operationId": "op_1a2b3c4d5e6f", "status": "PENDING", "resourceType": "USER", "operationType": "CREATE", "createdTime": "2025-12-01T15:30:45Z", "_links": { "self": { "href": "/operations/op_1a2b3c4d5e6f" } } } ``` When the operation completes, the structure includes additional fields: ```json { "operationId": "op_1a2b3c4d5e6f", "status": "COMPLETED", "resourceType": "USER", "resourceId": "hvOB1l3zDCaDAwp9tNLzZA", "operationType": "CREATE", "createdTime": "2025-12-01T15:30:45Z", "completedTime": "2025-12-01T15:30:52Z", "_links": { "self": { "href": "/operations/op_1a2b3c4d5e6f" }, "resource": { "href": "/users/hvOB1l3zDCaDAwp9tNLzZA" } } } ``` ### Polling Best Practices **Recommended Strategy**: - Start polling after 1-2 seconds - Poll no more frequently than once per second - Set a maximum timeout (5 minutes recommended) - Log operation IDs for troubleshooting **Typical Completion Times**: - Simple operations (create user, update settings): 2-5 seconds - Complex operations (bulk updates, with dependencies): 5-15 seconds - If operation exceeds 60 seconds, investigate for issues ### Code Example ### Handling Operation Failures If an operation fails (`status: "FAILED"`), the operation object includes an `error` field: ```json { "operationId": "op_1a2b3c4d5e6f", "status": "FAILED", "error": { "status": 400, "title": "Validation Error", "detail": "Invalid field value", "errors": [ { "field": "basicInfo.primaryEmail", "code": "VALIDATION_ERROR", "message": "Email address format is invalid" } ] } } ``` Review the error details to understand the failure cause, correct the issue, and resubmit if appropriate. --- ## Pagination Administration APIs use **scroll-based pagination** for consistent, efficient results when listing resources. This approach provides stable iteration even when data changes during the scroll. ### How Scroll-Based Pagination Works 1. **Initial Request**: Specify `pageSize` (default 100, max 1000) and optional filters/sorting 2. **Receive First Page**: Response includes `nextScrollId` if more results exist 3. **Subsequent Requests**: Pass `scrollId` parameter to get the next page 4. **Continue**: Repeat until `hasMore` is false ### Key Characteristics - **Opaque Scroll IDs**: Scroll IDs are opaque tokens - treat them as black boxes, don't try to decode or manipulate them - **Filter Encoding**: Filters and sort order are encoded in the scroll ID - **Consistent Results**: Even if data changes during iteration, you'll get consistent results for your scroll - **Immutable**: Don't modify filters or sort order mid-scroll (start new scroll instead) ### Pagination Response Structure ```json { "data": [ { /* resource object */ } ], "pagination": { "pageSize": 100, "hasMore": true, "nextScrollId": "abc123def456", "filter": "basicInfo.status==ACTIVE", "sort": "+lastName" }, "_links": { "self": { "href": "/users?pageSize=100" }, "next": { "href": "/users?pageSize=100&scrollId=abc123def456" } } } ``` ### Pagination Best Practices **Choosing Page Size**: - **Real-time/Interactive**: 50-100 items for quick responses - **Bulk Export/Processing**: 100-1000 items to reduce API calls - **Memory Constrained**: Smaller pages (25-50) to reduce memory usage - Balance between number of API calls and response size **Efficient Iteration**: - Process pages as they arrive (streaming) rather than buffering all results - Don't restart scrolls unnecessarily - they're expensive server-side - Cache scroll IDs if you need resumable operations - If scroll expires, start fresh from the beginning **Error Handling**: - If you receive a scroll expiration error, start a new scroll - Don't try to continue a failed scroll - begin fresh query ### Code Example --- ## Filtering with RSQL Administration APIs support powerful filtering using **RSQL (RESTful Service Query Language)** syntax. This enables precise resource searches without retrieving unnecessary data. ### RSQL Syntax Overview RSQL provides a URL-friendly query language for filtering API results. ### Comparison Operators - `==` equals - `!=` not equals - `>` greater than - `<` less than - `>=` greater than or equal - `<=` less than or equal ### Logical Operators - `;` AND condition (both must be true) - `,` OR condition (either can be true) - `()` grouping for complex expressions ### Special Features **Case Insensitivity**: Filter comparisons are case-insensitive by default. `name==jane` matches "jane", "Jane", or "JANE". **Wildcard Matching**: Use asterisk `*` for partial string matches: - `name==*smith` - ends with "smith" - `name==john*` - starts with "john" - `name==*doe*` - contains "doe" **Quoting Values**: Quote values containing spaces or special characters: - `department=='Research and Development'` - `title=='VP, Engineering'` ### Filter Examples **Simple Equality**: ```text status==ACTIVE ``` **Comparison Operators**: ```text createdTime>2025-11-01T00:00:00Z extensionNumber>=1000 ``` **Wildcard Search**: ```text name==*Support* email==*@example.com ``` **Logical AND (semicolon)**: ```text status==ACTIVE;department==Engineering ``` **Logical OR (comma)**: ```text department==Engineering,department==Sales ``` **Complex Expression**: ```text status==ACTIVE;(department==Engineering,department==Sales);createdTime>2025-01-01T00:00:00Z ``` This finds active resources in either Engineering or Sales created after January 1, 2025. ### Field Paths Use dot notation for nested fields. The exact field names depend on the API: **User Management API**: - `basicInfo.status` - user status - `basicInfo.primaryEmail` - user email - `directoryInfo.department` - user department - `basicInfo.createdTime` - creation timestamp **Ring Group Management API**: - `name` - ring group name - `extensionNumber` - extension number - `ringPattern` - distribution pattern - `ringTimeout` - ring duration Refer to each API's documentation for available filterable fields. ### Best Practices **Performance**: - Filter early and precisely to reduce data transfer - Avoid overly complex RSQL expressions (max 2000 characters) - Use specific field paths - Test filters with small page sizes first **Correctness**: - Match field names exactly (case-sensitive for field paths) - Use correct operators (`==` not `=`) - Quote values with spaces - Validate RSQL syntax before submitting - Check for matching parentheses in complex expressions ### Common Pitfalls ❌ **Wrong**: `status=ACTIVE` (single equals) ✅ **Correct**: `status==ACTIVE` (double equals) ❌ **Wrong**: `Status==ACTIVE` (wrong case for field name) ✅ **Correct**: `basicInfo.status==ACTIVE` (exact field path) ❌ **Wrong**: `department==Research and Development` (unquoted spaces) ✅ **Correct**: `department=='Research and Development'` (quoted) ### Code Example ### Further Reading For complete RSQL specification, see the [RSQL Parser documentation](https://github.com/jirutka/rsql-parser). --- ## Sorting Results Control the order of results using the `sort` query parameter. Sorting is applied before pagination, ensuring consistent ordering across all pages. ### Sort Syntax **Ascending Order**: Use `+` prefix or no prefix ```text sort=+lastName sort=lastName ``` **Descending Order**: Use `-` prefix ```text sort=-createdTime ``` **Multiple Fields**: Comma-separated for multi-level sorting ```text sort=+department,+lastName,+firstName ``` This sorts first by department (A-Z), then by last name (A-Z), then by first name (A-Z). ### Sort Examples **By Last Name (A-Z)**: ```text sort=+lastName ``` **By Creation Date (Newest First)**: ```text sort=-createdTime ``` **Multi-Level Sort**: ```text sort=+department,-createdTime ``` Sorts by department (A-Z), then within each department by creation date (newest first). ### Sortable Fields Available sort fields depend on the specific API. Common sortable fields: **User Management API**: - `basicInfo.lastName`, `basicInfo.firstName` - `basicInfo.createdTime`, `basicInfo.lastUpdatedTime` - `basicInfo.status` - `directoryInfo.department` **Ring Group Management API**: - `name` - `extensionNumber` - `ringPattern` Refer to each API's documentation for complete list of sortable fields. ### Best Practices - Use field paths that match the API's data model - Test single-field sorts before combining multiple fields - Combine with pagination for consistent iteration - Use appropriate ascending/descending based on use case ### Code Example --- ## Error Handling Administration APIs use **RFC 7807 Problem Details** format for structured, consistent error responses. This standard format makes error handling predictable across all APIs. ### Error Response Format All error responses follow this structure: ```json { "status": 400, "instance": "/users", "time": "2025-12-01T15:30:45Z", "title": "Request Validation Failed", "errors": [ { "field": "basicInfo.primaryEmail", "code": "VALIDATION_ERROR", "message": "Email address format is invalid" } ] } ``` ### Key Fields - **status**: HTTP status code (400, 401, 403, 404, 429, 500) - **title**: Human-readable error summary - **detail**: Specific explanation for this error occurrence - **errors**: Array of field-level validation errors (if applicable) - **field**: Specific field path where error occurred - **code**: Machine-readable error code for programmatic handling - **message**: Human-readable error message for this field - **requestId**, **responseId**: Tracking identifiers for support escalation ### Common HTTP Status Codes **Success Codes**: - `200 OK`: Successful GET request - `202 Accepted`: Async operation accepted (create, update, delete) **Client Error Codes**: - `400 Bad Request`: Validation error, malformed request, or invalid parameters - `401 Unauthorized`: Missing or invalid API key authentication - `403 Forbidden`: Insufficient permissions or customer ID mismatch - `404 Not Found`: Resource does not exist - `409 Conflict`: Resource conflict (e.g., duplicate extension number) - `429 Too Many Requests`: Rate limit exceeded **Server Error Codes**: - `500 Internal Server Error`: Unexpected server error ### Generic Error Codes **VALIDATION_ERROR** (400): - Field validation failed (format, length, pattern) - Required field missing - Invalid enum value **INVALID_FILTER** (400): - RSQL filter syntax error - Invalid field name in filter - Malformed filter expression **INVALID_SORT** (400): - Sort expression format error - Invalid field name for sorting - Unsupported sort direction **INVALID_PAGE_SIZE** (400): - pageSize out of range (must be 1-1000) **FORBIDDEN** (403): - Insufficient permissions - Customer ID mismatch (accessing another customer's resources) **NOT_FOUND** (404): - Resource does not exist - Malformed resource ID **RATE_LIMIT_EXCEEDED** (429): - Too many requests in time window - See Rate Limiting section for handling strategy ### Common Error Scenarios #### Scenario 1: Authentication Failure (401) **Request**: Missing or invalid API key ```http GET /users // x-api-key header missing or invalid ``` **Response**: ```json { "status": 401, "title": "Unauthorized", "detail": "Valid authentication credentials required" } ``` **Resolution**: Verify `x-api-key` header is present and contains valid API key. Regenerate key if expired. #### Scenario 2: Authorization Failure (403) **Request**: Valid API key but accessing another customer's resources **Response**: ```json { "status": 403, "title": "Forbidden", "detail": "Insufficient permissions to access this resource", "errors": [ { "code": "FORBIDDEN", "message": "Customer ID mismatch" } ] } ``` **Resolution**: Verify you're accessing resources for the correct customer ID. Check API key permissions in Admin Console. #### Scenario 3: Invalid RSQL Filter (400) **Request**: Malformed filter syntax ```http GET /users?filter=status=active ``` **Response**: ```json { "status": 400, "title": "Invalid Filter", "detail": "RSQL filter syntax is invalid", "errors": [ { "field": "filter", "code": "INVALID_FILTER", "message": "Use '==' for equality, not '='" } ] } ``` **Resolution**: Correct RSQL syntax: `filter=basicInfo.status==ACTIVE` #### Scenario 4: Invalid Sort Expression (400) **Request**: Unsupported sort field ```http GET /users?sort=invalidField ``` **Response**: ```json { "status": 400, "title": "Invalid Sort", "detail": "Sort field is not supported", "errors": [ { "field": "sort", "code": "INVALID_SORT", "message": "Field 'invalidField' cannot be used for sorting" } ] } ``` **Resolution**: Use valid sortable field from API documentation with proper field path. #### Scenario 5: Rate Limit Exceeded (429) **Request**: Too many requests in short period **Response**: ```json { "status": 429, "title": "Too Many Requests", "detail": "Rate limit exceeded", "errors": [ { "code": "RATE_LIMIT_EXCEEDED", "message": "Maximum 100 requests per minute exceeded" } ] } ``` **Response Headers**: ```http x-ratelimit-limit: 100 x-ratelimit-remaining: 0 x-ratelimit-reset: 1733073045 ``` **Resolution**: Wait until `x-ratelimit-reset` time or implement exponential backoff (see Rate Limiting section). ### Recovery Strategies **Transient Failures** (429, 500): - Implement exponential backoff: wait 1s, 2s, 4s, 8s, 16s between retries - Maximum 5 retry attempts recommended - Log failures for monitoring - Check status page if widespread **Permanent Failures** (400, 401, 403, 404, 409): - Do not retry automatically - Log error details with requestId/responseId - Fix input data or configuration - Alert administrators if unexpected **Validation Errors** (400 with field-level errors): - Review `errors` array for specific field issues - Correct invalid field values - Ensure required fields present - Validate data types and formats - Resubmit with corrected data ### Code Example --- ## Rate Limiting Administration APIs enforce rate limits to ensure fair usage and system stability. Understanding and respecting these limits is essential for reliable integrations. ### Current Rate Limits **Per API Key**: - **100 requests per minute**: Maximum sustained request rate - **10 concurrent async operations**: Maximum in-flight operations per organization - **Burst allowance**: Up to 20 requests in 10-second window (for bursty workflows) **Note**: Rate limits are subject to change. Monitor response headers for current values. ### Rate Limit Headers Every API response includes rate limit information in headers: ```http x-ratelimit-limit: 100 x-ratelimit-remaining: 87 x-ratelimit-reset: 1733073045 ``` - **x-ratelimit-limit**: Maximum requests allowed in current window - **x-ratelimit-remaining**: Requests remaining in current window - **x-ratelimit-reset**: Unix timestamp when rate limit resets ### Handling Rate Limits **Proactive Monitoring**: 1. Monitor `x-ratelimit-remaining` header in every response 2. Slow down requests when remaining count is low (< 10) 3. Implement client-side throttling before hitting limit **Reactive Handling** (429 Response): 1. Stop sending requests immediately 2. Parse `x-ratelimit-reset` from response 3. Calculate wait time: `reset_time - current_time` 4. Wait until reset time 5. Resume with exponential backoff: 2s, 4s, 8s, 16s **Exponential Backoff Pattern**: ```text Attempt 1: Wait 2 seconds Attempt 2: Wait 4 seconds Attempt 3: Wait 8 seconds Attempt 4: Wait 16 seconds Attempt 5: Wait 32 seconds Maximum: Give up or wait for reset ``` ### Best Practices **Distribute Load**: - Spread requests evenly over time rather than bursting - Use queuing on client side to control request rate - For bulk operations, implement client-side throttling (e.g., 1 request per second) **Optimize Request Patterns**: - Use appropriate page sizes to reduce number of requests - Filter results server-side rather than client-side - Cache responses when data doesn't change frequently - Batch updates in single requests where possible **Handle Limits Gracefully**: - Implement retry logic with exponential backoff - Log rate limit events for monitoring - Alert on repeated rate limiting (may indicate design issue) - Consider request priority (delay non-critical requests) ### Code Example --- ## Best Practices ### Understanding PUT Semantics :::danger CRITICAL: PUT Replaces the Entire Resource Administration APIs use PUT for updates, which requires the COMPLETE resource object. PUT operations are not partial updates. **Why This Matters**: - Omitted fields are interpreted as requests to remove that data - Missing sections could delete critical configurations - Partial submissions can cause unintended data loss **Correct Update Pattern**: 1. GET the complete current resource object - ⚠️ Use the get one `GET /entity/{id}` endpoint NOT the list/get all `GET /entity endpoint` for this as the payloads may be different. 2. Modify only the specific fields you want to change IN MEMORY 3. PUT the entire modified object back **Never Do This** (Partial PUT): ```text PUT /resources/{id} { "oneField": "newValue" } ``` This will REMOVE, RESET and/or DEPROVISON all other resource attributes potentially resulting in loss of service and/or data. **Always Do This** (Complete PUT): ```text GET /resources/{id} → Retrieve complete resource Modify in memory → resource.section.field = "newValue" PUT /resources/{id} with COMPLETE modified resource object ``` ::: ### Performance Optimization **Concurrent Operations**: - Submit operations in parallel but limit concurrency (5-10 requests recommended) - Monitor async operation queue depth - Implement client-side rate limiting before hitting API limits - Don't wait for one operation before starting next **Efficient Pagination**: - Choose appropriate page sizes based on use case: - Real-time/Interactive: 50-100 items - Bulk Export: 100-1000 items - Memory Constrained: 25-50 items - Process pages as they arrive (streaming) rather than buffering - Don't restart scrolls unnecessarily (expensive server-side) - Cache scroll IDs for resumable operations **Filtering Performance**: - Filter early and precisely to reduce data transfer - Avoid overly complex RSQL expressions - Test filters with small page sizes first - Use indexed fields when possible (status, timestamps) **Async Operation Polling**: - Start with 1-2 second initial poll interval - Increase interval with exponential backoff if operation is long-running - Set maximum poll duration (5 minutes recommended) - Most operations complete within 5-10 seconds **Caching Considerations**: - Cache relatively static data (sites, organizational structure) - Use appropriate TTL based on update frequency (15-60 minutes typical) - Invalidate cache on mutations - Consider cache staleness acceptable for your use case ### Security Best Practices **API Key Management**: - **Storage**: Use environment variables or secure vault systems (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault) - **Never Hardcode**: Don't embed keys in source code or configuration files - **Separation**: Use separate keys for dev, staging, production environments - **Rotation**: Rotate keys every 90 days as security best practice - **Revocation**: Implement immediate key revocation procedures for compromised keys - **Monitoring**: Monitor usage patterns for anomalies or suspicious activity - **Logging**: Never log full API keys - log last 4 characters only **Data Protection**: - **HTTPS Only**: All requests must use HTTPS (HTTP will be rejected) - **SSL Validation**: Validate SSL certificates in HTTP clients - **Sensitive Data**: Don't log sensitive data (emails, phone numbers, addresses, names) - **Encryption at Rest**: Implement encryption for data stored in your systems - **Data Retention**: Follow data retention policies (typically 7+ years for audit) - **Compliance**: Comply with applicable regulations (GDPR, CCPA, HIPAA if applicable) **Audit Logging**: - **Mutation Operations**: Log all create, update, delete operations with timestamps - **Operation IDs**: Record operation IDs for traceability - **Resource Changes**: Log resource IDs and affected fields (not sensitive values) - **Tamper-Proof**: Implement tamper-proof audit trails - **Retention**: Retain logs per compliance requirements (typically 7+ years) - **Monitoring**: Monitor logs for security events and anomalies **Access Control**: - **Least Privilege**: Apply principle of least privilege for API keys - **Scope Limiting**: Limit API key scope to required operations only - **Regular Review**: Review and revoke unused API keys regularly - **Service Accounts**: Use service accounts (not personal accounts) for API access - **IP Restrictions**: Consider IP whitelisting if your infrastructure allows **Monitoring and Alerting**: - Log all API requests and responses (excluding credentials and sensitive data) - Monitor for unusual patterns (spike in errors, unexpected deletes, off-hours access) - Alert on authentication failures - Track operation failure rates - Set up notifications for rate limit events ### Data Consistency **Handling Asynchronous Operations**: - Store operation IDs with source requests for correlation - Implement idempotency for safe retries where possible - Handle duplicate submissions gracefully - Verify completion before proceeding to dependent operations - Don't assume success - always check operation status **PUT Request Safety**: - **Critical**: PUT operations replace the entire resource - omitted fields may be set to null or default values - Always retrieve the complete resource with GET before updating - Modify only the fields you intend to change, preserving all others - Validate the complete payload includes all required fields before sending PUT - Example pitfall: Omitting `phoneNumber` in a PUT request may delete the user's phone number even if you only intended to update their email - When updating a single field, consider if the API supports PATCH instead of PUT - Test PUT operations in development with complete payloads to avoid data loss **Update Safety**: - Always GET complete resource before updating - Never submit partial resource objects (unless API explicitly supports PATCH) - Validate all required fields present after modification - Consider optimistic locking for concurrent updates if available - Verify updates completed successfully before proceeding **Error Recovery**: - Distinguish between transient and permanent failures - Implement appropriate retry logic for transient failures - Log all failures with sufficient detail for debugging - Have rollback procedures for failed bulk operations --- ## Troubleshooting ### Authentication Issues **Problem**: 401 Unauthorized errors **Debugging Steps**: 1. Verify `x-api-key` header is set correctly (case-sensitive) 2. Check for whitespace or encoding issues in key value 3. Confirm API key hasn't been revoked in Admin Console 4. Test with curl to isolate client library issues: ```bash curl -H "x-api-key: YOUR_KEY" \ -H "Accept: application/vnd.users.v1+json" \ https://api.8x8.com/admin-provisioning/users?pageSize=1 ``` 5. Regenerate API key if needed **Problem**: 403 Forbidden despite valid key **Debugging Steps**: 1. Verify API key has required API products enabled 2. Check account status in Admin Console 3. Confirm accessing resources for correct customer organization 4. Verify no IP restrictions blocking requests 5. Contact support if issue persists ### Pagination Issues **Problem**: Inconsistent results between pages **Cause**: Data changed during scroll, or filter was modified **Solution**: Don't modify data or filters during active scroll. Start fresh scroll if data changed. **Problem**: Duplicate resources across pages **Cause**: Shouldn't occur with scroll-based pagination (report to support if seen) **Solution**: File support ticket with scroll ID and example duplicates. ### Async Operation Problems **Problem**: Operation stuck in PENDING or IN_PROGRESS **Debugging Steps**: 1. Check if operation timeout exceeded (> 5 minutes) - may indicate system issue 2. Verify no prior rate limiting (check for 429 responses) 3. Check [8x8 status page](https://status.8x8.com) for incidents 4. Review operation details for error messages 5. Contact support with operation ID if persists > 10 minutes **Problem**: Operation fails with generic error **Debugging Steps**: 1. Check operation response `error` field for details 2. Verify input data was valid (all required fields, correct formats) 3. Test with minimal request (only required fields) 4. Check for dependency issues (invalid IDs, missing prerequisites) 5. Review requestId/responseId in error response for support escalation **Problem**: Operation completes but resource not visible **Debugging Steps**: 1. Verify operation status is COMPLETED (not just IN_PROGRESS) 2. Check resource status/state is as expected 3. Clear Admin Console cache (hard refresh browser) 4. Verify viewing correct organization/site filter 5. Use GET directly with resource ID to confirm existence ### Filter and Query Issues **Problem**: RSQL filter returns no results unexpectedly **Debugging Steps**: 1. Test without filter to verify resources exist 2. Simplify filter to one condition at a time 3. Check field names match API exactly (case-sensitive for paths) 4. Verify enum values match exactly (e.g., `ACTIVE` not `active`) 5. Use correct operators (`==` for equality, not `=`) 6. Quote values with spaces: `department=='Research and Development'` **Problem**: "Invalid filter syntax" errors **Solutions**: - Check parentheses matching: `(condition1,condition2)` - Use semicolon for AND: `status==ACTIVE;department==Engineering` - Use comma for OR: `department==Engineering,department==Sales` - Escape special characters if needed - Refer to [RSQL specification](https://github.com/jirutka/rsql-parser) for complex queries **Problem**: Sorting not working as expected **Solutions**: - Use correct field paths with + or - prefix: `+lastName` (ascending) or `-lastName` (descending) - Verify field is sortable (not all fields support sorting) - Test with single sort field first before combining ### Performance Issues **Problem**: Slow API responses **Debugging Steps**: 1. Check page size - large pages (500-1000) take longer 2. Simplify complex RSQL filters 3. Verify network latency (test from different locations) 4. Check for high request concurrency (reduce parallel requests) 5. Monitor rate limit headers for throttling **Problem**: Timeouts during bulk operations **Solutions**: - Reduce concurrency (5-10 parallel requests maximum) - Implement proper backoff between requests - Break large batches into smaller chunks - Monitor API status page during bulk operations - Use async operations correctly (don't block waiting) --- ## Support Resources ### Documentation **API Documentation**: - [8x8 Admin Console](https://admin.8x8.com) - Individual API OpenAPI Specifications (see each API guide) **Technical References**: - [RSQL Specification](https://github.com/jirutka/rsql-parser) - [RFC 7807 - Problem Details for HTTP APIs](https://tools.ietf.org/html/rfc7807) - [ISO 8601 Date Format](https://en.wikipedia.org/wiki/ISO_8601) - [E.164 Phone Number Format](https://en.wikipedia.org/wiki/E.164) ### Service Status - [8x8 Status Page](https://status.8x8.com) ### Getting Support **When to Contact Support**: - Operation stuck for > 10 minutes - Repeated 500 errors - Unexpected authentication/authorization failures - Data inconsistencies or corruption - Questions about API behavior or capabilities **Required Information When Contacting Support**: - **Operation ID**: For async operation issues - **Request ID and Response ID**: From error response (requestId, responseId fields) - **Timestamp**: Of the issue occurrence (UTC timezone) - **API Key**: Last 4 characters only (never full key) - **Complete Error Response**: Sanitize sensitive data, include all fields - **Steps to Reproduce**: Detailed steps to replicate the issue - **Frequency**: One-time, intermittent, or constant - **Environment**: Development, staging, or production - **Recent Changes**: Any recent changes to integration code **How to Contact Support**: - **Admin Console**: User profile menu → Contact Support - For production-critical issues, mention "Production Critical" in subject **Before Contacting Support**: 1. Check [8x8 Status Page](https://status.8x8.com) for known incidents 2. Review this troubleshooting section 3. Test with curl to isolate client library issues 4. Verify API key permissions and status 5. Collect logs and error responses with requestId/responseId --- **Version**: 1.0 | **Last Updated**: January 16, 2026 | **Applies to**: All Administration API Suite APIs --- ## User Management API Guide **API Version**: 1.0 | **Last Updated**: January 15, 2026 | **Part of**: [Administration API Suite](./suite-common.mdx) import TabbedExternalCodeSample from '@site/docusaurus/components/TabbedExternalCodeSample'; :::warning BETA - Limited Access **These Administration APIs are currently in Beta testing.** API keys cannot be generated in Admin Console at this point. Only Beta program participants may use these APIs. ::: ## Table of Contents 1. [Overview](#overview) 2. [Prerequisites & Authentication](#prerequisites--authentication) 3. [Quickstart](#quickstart) 4. [Core Concepts](#core-concepts) 5. [Use Cases](#use-cases) 6. [API Reference](#api-reference) 7. [Business Rules](#business-rules) 8. [API-Specific Error Scenarios](#api-specific-error-scenarios) 9. [API-Specific Troubleshooting](#api-specific-troubleshooting) 10. [Additional Resources](#additional-resources) --- ## Overview The 8x8 User Management API provides programmatic access to user lifecycle management within your 8x8 organization. This RESTful API enables you to automate the complete lifecycle of users, from creation through configuration to deletion, supporting integration with HR systems, identity providers, and custom automation workflows. ### What You Can Do - Create and configure users with comprehensive profile information - Search and filter users using powerful RSQL query syntax - Update user details, assignments, and service configurations - Delete users when they leave your organization ### Primary Use Cases **Employee Lifecycle Automation**: Integrate with HR systems (Workday, BambooHR, SAP SuccessFactors) to automatically provision and deprovision users. **Site Deployment**: Bulk provision users for new office locations or business units. **Directory Synchronization**: Synchronize user information with external identity providers and organizational directories. ### API Architecture - **RESTful Design**: Standard HTTP methods (GET, POST, DELETE) with JSON payloads - **Asynchronous Operations**: Create and delete operations use async processing (see [API Essentials - Async Operations](./suite-common.mdx#asynchronous-operations)) - **RSQL Filtering**: Powerful query syntax for precise user searches (see [API Essentials - RSQL](./suite-common.mdx#filtering-with-rsql)) - **Scroll-Based Pagination**: Efficient pagination for large result sets (see [API Essentials - Pagination](./suite-common.mdx#pagination)) ### Reference View the [API Reference](#api-reference) section below for detailed endpoint documentation. --- ## Prerequisites & Authentication Before using the User Management API, review the **[Administration API Essentials](./suite-common.mdx)**, which covers: - **Prerequisites**: Account requirements, API credential acquisition, technical requirements - **Getting Started**: Step-by-step credential setup and testing - **Authentication**: API key authentication with `x-api-key` header - **API Versioning**: Version negotiation via the `Content-Type` header (on writes) and the `Accept` header (on reads) - **Common Patterns**: Asynchronous operations, pagination, filtering, sorting - **Error Handling**: RFC 7807 format and common error scenarios - **Rate Limiting**: Request limits and handling strategies - **Best Practices**: Performance, security, and data consistency - **Troubleshooting**: Common issues and debugging steps - **Support Resources**: Contact information and escalation procedures ### User Management API Specifics **Version header** for this API (see [API Versioning](./suite-common.mdx#api-versioning) for the full rule): - Requests with a payload (`POST`, `PUT`) carry the version in `Content-Type: application/vnd.users.v1+json` - Requests that return data (`GET`) carry it in `Accept: application/vnd.users.v1+json` **Required API Products** (when creating API key in Admin Console): - **UC & CC Admin Operations**: Required for user management operations - **UC & CC User Management**: Required for user-specific operations **User-Specific Prerequisites**: - Understanding of 8x8's organizational hierarchy (Customer → PBX → Site → User) - Knowledge of your site IDs (obtain from Admin Console → Sites) - Familiarity with user roles and permissions within 8x8 --- ## Quickstart This quickstart demonstrates your first successful interaction with the User Management API. You'll create a user, monitor the operation to completion, and retrieve the created user's details. **Prerequisite Knowledge**: This quickstart assumes you've reviewed the [Common Documentation](./suite-common.mdx) and understand: - API key authentication - Asynchronous operation patterns - How to poll operation status ### What You'll Accomplish 1. Create a new user with basic information 2. Receive an operation ID and poll until completion 3. Retrieve the fully created user object **Time to Complete**: Approximately 5 minutes ### Prerequisites for Quickstart - API key obtained and tested (see [API Essentials - Getting Started](./suite-common.mdx#getting-started)) - Valid Site ID from your organization (obtain from Admin Console → Sites or lookup via the [Site Management API](./site-management-api-guide)) - Basic user information ready (name, email, username) ### Step 1: Create a User User creation is an asynchronous operation (see [API Essentials - Async Operations](./suite-common.mdx#asynchronous-operations) for details). The API immediately returns a 202 Accepted response with an operation ID that you'll use to track progress. **Required Fields**: - `basicInfo.userName`: Unique login username (3-70 characters) - `basicInfo.firstName`: User's first name (2-128 characters) - `basicInfo.lastName`: User's last name (2-30 characters) - `basicInfo.primaryEmail`: Primary contact email (valid email format) - `basicInfo.site.id`: Site assignment (valid site ID from your organization) **Optional but Recommended**: - `basicInfo.status`: User account status (ACTIVE or INACTIVE) - `basicInfo.locale`: Language preference (e.g., en-US, en-GB) - `basicInfo.timezone`: Timezone for call recordings and queues **Expected Response** (202 Accepted): ```json { "operationId": "op_1a2b3c4d5e6f", "status": "PENDING", "resourceType": "USER", "operationType": "CREATE", "createdTime": "2025-12-01T15:30:45Z", "_links": { "self": { "href": "/operations/op_1a2b3c4d5e6f" } } } ``` The operation ID (`op_1a2b3c4d5e6f` in this example) is your tracking token for monitoring progress. ### Step 2: Poll Operation Status Since user creation happens asynchronously, you need to check the operation status periodically until it completes. See [API Essentials - Async Operations](./suite-common.mdx#asynchronous-operations) for complete polling guidance. **Polling Strategy**: - Start checking after 1 second - Check no more frequently than once per second - Set a maximum timeout (5 minutes recommended) **Expected Response** (Operation Completed): ```json { "operationId": "op_1a2b3c4d5e6f", "status": "COMPLETED", "resourceType": "USER", "resourceId": "hvOB1l3zDCaDAwp9tNLzZA", "operationType": "CREATE", "createdTime": "2025-12-01T15:30:45Z", "completedTime": "2025-12-01T15:30:52Z", "_links": { "self": { "href": "/operations/op_1a2b3c4d5e6f" }, "resource": { "href": "/users/hvOB1l3zDCaDAwp9tNLzZA" } } } ``` When `status` is `COMPLETED`, the `resourceId` field contains your new user's ID. ### Step 3: Retrieve the Created User Once the operation completes, retrieve the full user object to confirm all details were created correctly. **Expected Response** (200 OK): ```json { "basicInfo": { "userId": "hvOB1l3zDCaDAwp9tNLzZA", "customerId": "0012J00042NkZQIQA3", "createdTime": "2025-12-01T15:30:52Z", "lastUpdatedTime": "2025-12-01T15:30:52Z", "userName": "jane.smith@example.com", "firstName": "Jane", "lastName": "Smith", "status": "ACTIVE", "locale": "en-US", "timezone": "America/Los_Angeles", "primaryEmail": "jane.smith@example.com", "site": { "id": "SAH3U8guQaK4WQhpDZi0rQ", "name": "Headquarters", "pbxName": "corpco01" } }, "directoryInfo": { "displayInDirectory": true }, "serviceInfo": { "licenses": [], "extensions": [] }, "assignmentInfo": { "ringGroups": [], "ucCallQueues": [], "userGroups": [] } } ``` ### Quickstart Complete You've successfully: - ✅ Created a user via the API - ✅ Monitored an asynchronous operation to completion - ✅ Retrieved the created user's details **Next Steps**: - Explore [Use Cases](#use-cases) for more complex scenarios - Learn about [Core Concepts](#core-concepts) for deeper understanding - Review [API Essentials - Best Practices](./suite-common.mdx#best-practices) for production implementations --- ## Core Concepts Understanding these foundational concepts specific to the User Management API will help you use it effectively. For general concepts (asynchronous operations, pagination, filtering, sorting, error handling), see [Administration API Essentials](./suite-common.mdx). ### User Object Structure Users in 8x8 have a comprehensive data model organized into four logical sections: #### 1. basicInfo (Required) Core identity and account information: - **userId**: Unique system-generated identifier (read-only) - **customerId**: Organization identifier (read-only) - **userName**: Login username (required, 3-70 characters, unique) - **firstName**: User's given name (required, 2-128 characters) - **lastName**: User's family name (required, 2-30 characters) - **primaryEmail**: Primary contact email (required, valid email format) - **status**: Account status - ACTIVE or INACTIVE - **locale**: Language for UI, prompts, and notifications - **timezone**: Time zone for recordings and queue views - **site**: Physical location assignment (critical for routing and features) - **createdTime**, **lastUpdatedTime**: Audit timestamps (read-only) - **scimProvider**, **ssoProvider**, **ssoFederationId**: Identity integration fields #### 2. directoryInfo (Optional) Directory and organizational profile: - **jobTitle**: User's role or position - **department**: Organizational unit - **directoryScope**: Visibility scope (CUSTOMER, PBX, or SITE) - **displayInDirectory**: Whether user appears in searches - **personalPhoneNumbers**: Additional contact numbers array #### 3. serviceInfo (Optional) Service assignments and configurations: - **licenses**: Array of assigned licenses/subscriptions - **extensions**: Array of voice extensions with complete configuration: - Extension numbers and phone numbers - Call routing and forwarding settings - Voicemail configuration - Device assignments - Caller ID settings - Recording preferences - Emergency address information #### 4. assignmentInfo (Optional) Group memberships and policies: - **profilePolicy**: Applied user profile policy - **ringGroups**: Ring group memberships - **ucCallQueues**: Call queue assignments - **userGroups**: User group memberships ### 8x8 Organizational Hierarchy Users exist within a hierarchical structure that affects routing, features, and management: ```text Customer (Organization) └── PBX (Platform Instance) └── Site (Physical Location) └── User (Individual Account) └── Extension (Voice Service) ``` **Key Points**: - Every user must be assigned to a Site - Site determines PBX assignment automatically - Multiple sites can share a PBX - Extensions provide telephony capabilities - Hierarchy affects feature availability and call routing --- ## Use Cases This section covers the most common real-world scenarios for using the User Management API, with implementation patterns and code examples. For common patterns like pagination, filtering, and error handling, see [Administration API Essentials](./suite-common.mdx). ### Use Case 1: Employee Onboarding Automation **Business Context**: When new employees join your organization, their user accounts need to be created quickly and configured consistently. Manual provisioning through the Admin Console is time-consuming and error-prone at scale. Integrating with your HR system (Workday, BambooHR, SAP SuccessFactors) enables automatic user creation when employees are added. **When to Use**: - Integrating HR system with 8x8 for automated provisioning - Reducing IT workload for routine user creation - Ensuring consistent user configuration across the organization - Supporting rapid scaling or seasonal hiring **Implementation Pattern**: 1. **Receive Employee Data**: HR system triggers webhook or scheduled sync provides employee details 2. **Map to User Schema**: Transform HR data to User API schema (userName from email, names, department, site assignment) 3. **Create User**: POST to `/users` with complete user information 4. **Monitor Operation**: Poll operation status until completion (see [API Essentials - Async Operations](./suite-common.mdx#asynchronous-operations)) 5. **Handle Results**: Log success, retry on transient failures, alert on business logic errors 6. **Notify Stakeholders**: Send welcome email, create tickets for additional setup (hardware, access cards) **Required Information**: - Employee first name, last name, email - Valid site ID for office location - Department, job title for directory - Desired status (ACTIVE for immediate access, INACTIVE for pre-boarding) **Expected Outcomes**: - User account created in 8x8 within minutes of HR system entry - Consistent configuration based on role and location - Audit trail of all provisioning actions - Reduced IT workload and faster employee onboarding **Common Challenges**: - **Site ID Mapping**: Maintain mapping between HR office locations and 8x8 site IDs - **Username Conflicts**: Handle duplicates with numbering scheme (e.g., jane.smith2) - **Partial Failures**: User created but downstream operations fail (licenses, extensions) - **Data Quality**: Validate and clean HR data before submission --- ### Use Case 2: Bulk User Management **Business Context**: When deploying a new office location or migrating from another platform, you need to create dozens or hundreds of users quickly. Sequential creation through the UI or individual API calls is inefficient. Bulk operations enable rapid deployment while maintaining consistency. **When to Use**: - New site/office deployments - Platform migrations - Mergers and acquisitions - Organizational restructuring - Seasonal workforce scaling **Implementation Pattern**: 1. **Prepare Data**: Create CSV/Excel with user information, validate completeness 2. **Validate Site IDs**: Ensure all referenced sites exist 3. **Submit in Batches**: Create users in parallel (5-10 concurrent requests recommended, see [API Essentials - Best Practices](./suite-common.mdx#best-practices)) 4. **Track Operations**: Store operation IDs with corresponding user data 5. **Monitor Progress**: Poll all operations, implement retry logic for failures 6. **Generate Report**: Summary of successful vs failed creations with error details **Pagination for Discovery**: When you need to retrieve all users (for reporting, migration, or synchronization), use scroll-based pagination (see [API Essentials - Pagination](./suite-common.mdx#pagination)): **Expected Outcomes**: - Rapid user creation (dozens per minute) - Consistent configuration across all users - Clear audit trail of bulk operation - Detailed success/failure reporting **Best Practices**: - **Batch Size**: Create 5-10 users concurrently (more may hit rate limits, see [API Essentials - Rate Limiting](./suite-common.mdx#rate-limiting)) - **Operation Tracking**: Store operation IDs with source data for correlation - **Error Handling**: Distinguish validation errors (fix data) from transient failures (retry per [API Essentials - Error Handling](./suite-common.mdx#error-handling)) - **Progress Monitoring**: Implement progress indicators for long-running operations - **Rollback Planning**: Have process to delete bulk-created users if validation fails --- ### Use Case 3: User Information Updates **Business Context**: Employee information changes frequently - promotions, department transfers, name changes, office relocations. Keeping 8x8 user data synchronized with HR systems and organizational directories requires systematic updates. Manual updates are error-prone and don't scale. **When to Use**: - Employee role or department changes - Office relocations or site reassignments - Name changes or contact information updates - Service assignment modifications - Directory information corrections :::danger CRITICAL: Understanding PUT Semantics The User Management API does NOT support partial updates via PATCH. PUT operations require the COMPLETE user object. Be sure to familiarise yourself with the correct update pattern in the [Administration API Essentials - Understanding PUT Semantics](./suite-common.mdx#understanding-put-semantics) section to avoid unintended data loss. ::: **Implementation Pattern**: 1. **Identify User**: Get userId from sync system or search by email/username 2. **Retrieve Current State**: GET `/users/{userId}` for complete user object 3. **Apply Changes**: Modify specific fields in memory (never submit partial objects) 4. **Validate Changes**: Ensure required fields still present, data types correct 5. **Submit Update**: PUT complete modified user object (not currently supported in API - only POST and DELETE are implemented) 6. **Monitor Operation**: Poll until completion 7. **Verify**: GET user again to confirm changes applied **Expected Outcomes**: - User information updated accurately - No data loss from incomplete submissions - Audit trail of changes - Changes reflected in Admin Console immediately after operation completes --- ### Use Case 4: Employee Offboarding **Business Context**: When employees leave, their accounts must be deactivated or removed to maintain security and license compliance. Automated offboarding ensures timely deprovisioning when HR systems record terminations, eliminating security risks from forgotten accounts. **When to Use**: - Employee termination or resignation - Contractor end-of-engagement - Account cleanup for inactive users - License reclamation - Security and compliance requirements **Implementation Pattern**: Most organizations use a three-stage offboarding process rather than immediate deletion: **Stage 1: Immediate Disable** (Termination Day) - Use PUT to change `basicInfo.status` to `INACTIVE` - Disables login immediately while preserving all user data - Allows time for knowledge transfer and data retrieval - User remains visible in directory but cannot access services **Stage 2: License Deprovision** (After Grace Period, typically 3-7 days) - Use PUT to remove licenses and extensions from the user - Frees licenses for reassignment to other users - User record remains but has no service access - Typically triggered by offboarding process milestone **Stage 3: Account Deletion** (After Retention Period, typically 30-90 days) - Use DELETE `/users/{userId}` to permanently remove user - Complies with data retention policies - Frees up the username for potential reuse - Final cleanup of user record **Why Three Stages?**: - **Compliance**: Maintains audit trail during retention period - **Operational**: Allows IT to retrieve data, transfer ownership, update documentation - **License Management**: Immediate license recovery without data loss - **Reversibility**: Can reactivate user if termination reversed (before Stage 3) **Expected Outcomes**: - User account disabled immediately (INACTIVE status) - Login access revoked within minutes - Permanent deletion after retention period - License freed for reassignment - Audit trail for compliance --- ## Business Rules This section documents important constraints and special behaviors specific to user management operations. ### Site Assignment Constraints **Site is Immutable**: The `basicInfo.site` field cannot be modified after user creation. - Site assignment is permanent and cannot be changed via PUT - Users requiring site reassignment must be deleted and recreated - Plan site assignments carefully during initial user creation **Workaround for Site Changes**: If a user must be moved to a different site: 1. Document current user configuration (GET user, save complete response) 2. Delete the user (DELETE operation) 3. Recreate user with new site ID (POST operation with same data, different site) 4. Note: User ID will change - update any external system references ### Directory Visibility Multiple attributes control directory visibility with complex interactions: **Attributes**: - `directoryInfo.displayInDirectory` - `serviceInfo.extensions[].displayInDirectory` **Visibility Rules**: **Users without licenses**: The `directoryInfo.displayInDirectory` attribute controls whether the user is visible at all in any directory, including any personal contact numbers. **Users with UC license**: The `serviceInfo.extensions[].displayInDirectory` attribute for the extension object that has `serviceInfo.extensions[].extensionType = "UC"` and the `directoryInfo.displayInDirectory` attributes are synchronized (i.e., toggling one will toggle the other), controlling visibility of all numbers of the user except for their CC agent details (personal numbers, UC extension numbers, and fax numbers). **Users with CC license**: The `serviceInfo.extensions[].displayInDirectory` attribute for the extension object that has `serviceInfo.extensions[].extensionType = "CC"` controls whether their contact center agent details are visible. ### User Group Assignment The `assignmentInfo.userGroups` field is structured as an array, but users can only belong to one user group at a time. **Behavior**: - If multiple user groups are provided in the array, the user will be assigned to the last group processed - The processing order is not guaranteed, making the result unpredictable when multiple groups are specified - Previous group assignments are overwritten, not accumulated **Best Practice**: Provide a maximum of one user group in the `assignmentInfo.userGroups` array to ensure predictable assignment behavior. **Example** (Correct - Single User Group): ```json { "assignmentInfo": { "userGroups": [ { "id": "ug_abc123" } ] } } ``` **Example** (Avoid - Multiple User Groups): ```json { "assignmentInfo": { "userGroups": [ { "id": "ug_abc123" }, { "id": "ug_def456" } // Only one of these will be assigned ] } } ``` ### DID License Assignment DID (Direct Inward Dialing) licenses have special handling: - **Not assigned explicitly**: Don't include DID licenses in POST or PUT requests - **Automatic assignment**: DID licenses are assigned automatically when phone numbers are configured - **Visible on GET**: DID licenses will appear in GET responses after phone number configuration ### License Changes Changing a user's license type (e.g., upgrading from an X2 to an X4 license) cannot be accomplished in a single PUT operation. **Two-Step Update Required**: 1. **Remove Existing License**: Submit a PUT request with the `serviceInfo.licenses` array empty or with the current license removed 2. **Apply New License**: Submit a second PUT request with the new license configuration **Data Loss Warning**: :::warning Extension Data Loss When licenses are removed and reapplied, certain extension-related data will be permanently lost: - **Group Memberships**: Ring group and call queue assignments will be removed - **Voicemail Setup**: Voicemail greetings, PIN, and configuration settings will be reset - **Voicemail Messages**: Existing voicemail recordings will be deleted Plan accordingly and inform users before making license changes. Consider exporting or documenting critical configurations before proceeding. ::: **Example Workflow**: ```text Step 1: GET /users/{userId} → Retrieve current user state Step 2: PUT /users/{userId} → Submit with licenses removed Step 3: Poll operation until COMPLETED Step 4: PUT /users/{userId} → Submit with new license applied Step 5: Poll operation until COMPLETED Step 6: Reconfigure group memberships, voicemail, etc. as needed ``` ### Dial Plan Country Codes When configuring extensions, the `serviceInfo.extensions[].dialPlanCallingCountry` attribute can take one of the values from the table below. If omitted the Site default value will be set. **Note** Ensure the dial plan calling country matches the country of the primary phone number to ensure correct outbound calling. | Country | Dial Plan Calling Country | |---------|----------------| | Angola | AGONP | | Argentina | ARGNP | | Australia | AUSTNP | | Austria | AUSTRIANP | | Bahrain | BHRNP | | Belgium | BLGMNP | | Brazil | BRAZILNP | | Bulgaria | BGRNP | | Chile | CHLNP | | China | CHNNP | | Colombia | CLMBANP | | Costa Rica | CRINP | | Croatia | HRVNP | | Czech Republic | CZENP | | Denmark | DNKNP | | Ecuador | ECUNP | | Estonia | ESTNP | | Finland | FINLNDNP | | France | FRANCENP | | Germany | GERNP | | Greece | GRCNP | | Hong Kong | HKNP | | Hungary | HGRYNP | | India | INDNP | | Indonesia | IDNNP | | Ireland | IRELANDNP | | Israel | ISRAELNP | | Italy | ITALYNP | | Japan | JAPANNP | | Kazakhstan | KZNP | | Kenya | KENNP | | Latvia | LVANP | | Lithuania | LTUNP | | Luxembourg | LUXNP | | Malaysia | MLYNP | | Malta | MLTNP | | Mexico | MXNP | | Netherlands | NTHRLNDNP | | New Zealand | NZLNP | | North America | NANP | | Norway | NORNP | | Panama | PANNP | | Peru | PERNP | | Philippines | PHNP | | Poland | PLNDNP | | Portugal | PRTNP | | Romania | RMNNP | | Russia | RUSNP | | Singapore | SINGNP | | Slovakia | SVKNP | | Slovenia | SVNNP | | South Africa | STHAFNP | | South Korea | KORNP | | Spain | SPNNP | | Sri Lanka | SRILANKNP | | Sweden | SWEDENNP | | Switzerland | SWITZNP | | Taiwan | TWNNP | | Thailand | THANP | | Turkey | TRKYNP | | UK | UKNP | | Ukraine | UKRNP | | United Arab Emirates | ARENP | | Vietnam | VTNMNP | --- ## API Reference ### Endpoints Overview | Endpoint | Method | Purpose | Async? | Authentication | |----------|--------|---------|--------|----------------| | `/users` | GET | Search/list users with filtering and pagination | No | Required | | `/users` | POST | Create new user | Yes | Required | | `/users/{userId}` | GET | Retrieve specific user details | No | Required | | `/users/{userId}` | DELETE | Delete user | Yes | Required | **Note**: PUT (update) operations are documented in the OpenAPI specification but not currently implemented in v1.0.0. Updates require delete and recreate workflow. ### Base URL `https://api.8x8.com/admin-provisioning` ### Required Headers See [API Essentials - Common Request Patterns](./suite-common.mdx#common-request-patterns) for complete header requirements. For User Management API specifically: ```http x-api-key: your-api-key-here Content-Type: application/vnd.users.v1+json (on POST/PUT — carries the version) Accept: application/vnd.users.v1+json (on GET) ``` ### Query Parameters **Pagination Parameters** (GET `/users`): - `pageSize` (integer, 1-1000): Items per page, default 100 - `scrollId` (string): Continuation token for next page (see [API Essentials - Pagination](./suite-common.mdx#pagination)) - `filter` (string): RSQL filter expression (see [API Essentials - RSQL Filtering](./suite-common.mdx#filtering-with-rsql)) - `sort` (string): Sort criteria (e.g., `+lastName,-firstName`, see [API Essentials - Sorting](./suite-common.mdx#sorting-results)) **Path Parameters**: - `userId` (string): Unique user identifier (e.g., `hvOB1l3zDCaDAwp9tNLzZA`) ### HTTP Status Codes See [API Essentials - Error Handling](./suite-common.mdx#error-handling) for complete status code descriptions. **Success Codes**: - `200 OK`: Successful GET request - `202 Accepted`: Async operation accepted (create, delete) **Error Codes**: - `400 Bad Request`: Validation error or malformed request - `401 Unauthorized`: Missing or invalid authentication - `403 Forbidden`: Insufficient permissions - `404 Not Found`: User does not exist - `429 Too Many Requests`: Rate limit exceeded (see [API Essentials - Rate Limiting](./suite-common.mdx#rate-limiting)) - `424 Failed Dependency`: Downstream service failure - `500 Internal Server Error`: Unexpected server error ### Key Data Models **User Object** (Complete): - `basicInfo` (required): Core identity fields (see [Core Concepts](#user-object-structure)) - `directoryInfo` (optional): Organizational profile - `serviceInfo` (optional): Service assignments - `assignmentInfo` (optional): Group memberships **Operation Object**: See [API Essentials - Asynchronous Operations](./suite-common.mdx#asynchronous-operations) for complete operation object description. **UserPage Object** (List Response): See [API Essentials - Pagination](./suite-common.mdx#pagination) for pagination response structure. ### Field Constraints **Required Fields** (basicInfo): - `userName`: 3-70 characters, pattern: `[A-Za-z0-9.@\-_/]+` - `firstName`: 2-128 characters, pattern: `[A-Za-z0-9.,\-_()' ]+` - `lastName`: 2-30 characters, pattern: `[A-Za-z0-9.,\-_()' ]+` - `primaryEmail`: 5-128 characters, valid email format **Common Field Formats**: - Dates: ISO 8601 format (`2025-12-01T15:30:45Z`) - Phone Numbers: E.164 format (`+14085551234`) - Status Values: ACTIVE, INACTIVE - Locale: ISO codes (en-US, en-GB, fr-FR, etc.) ### OpenAPI Specification View the complete [OpenAPI Specification](/administration/user-api-v1.yaml) for detailed endpoint documentation. ## API-Specific Error Scenarios For common error handling patterns, see [Administration API Essentials - Error Handling](./suite-common.mdx#error-handling). This section covers error scenarios specific to the User Management API. ### Missing Required Fields **Request**: Create user without email ```json POST /users { "basicInfo": { "userName": "jsmith", "firstName": "John", "lastName": "Smith" // primaryEmail missing } } ``` **Response** (400 Bad Request): ```json { "status": 400, "title": "Validation Error", "detail": "Required fields are missing", "errors": [ { "field": "basicInfo.primaryEmail", "code": "VALIDATION_ERROR", "message": "primaryEmail is required" } ] } ``` **Resolution**: Add missing required field and resubmit. ### Duplicate Username **Request**: Create user with existing userName ```json POST /users { "basicInfo": { "userName": "jsmith", "firstName": "Jane", "lastName": "Smith", "primaryEmail": "jane.smith@example.com" } } ``` **Operation Response** (status: FAILED): ```json { "operationId": "op_1a2b3c4d5e6f", "status": "FAILED", "error": { "status": 400, "title": "Duplicate Username", "detail": "User with this username already exists", "errors": [ { "field": "basicInfo.userName", "code": "VALIDATION_ERROR", "message": "Username jsmith is already in use" } ] } } ``` **Resolution**: Use unique username or delete existing user first. ### User-Specific Validation Errors **Invalid Field Patterns**: - Username must match pattern `[A-Za-z0-9.@\-_/]+` - First name must match pattern `[A-Za-z0-9.,\-_()' ]+` - Email must be valid email format - Locale must be valid ISO locale code **Invalid Site Assignment**: - Site ID must exist in the organization - Site must be active and accessible --- ## API-Specific Troubleshooting For common troubleshooting guidance, see [Administration API Essentials - Troubleshooting](./suite-common.mdx#troubleshooting). This section covers troubleshooting issues specific to the User Management API. ### User Not Visible After Creation **Problem**: Operation completes but user not visible in Admin Console **Debugging Steps**: 1. Verify operation status is `COMPLETED` (not just `IN_PROGRESS`) 2. Check user `basicInfo.status` is `ACTIVE` 3. Clear Admin Console cache (hard refresh browser) 4. Verify viewing correct site filter in Admin Console 5. Use `GET /users/{userId}` directly to confirm user exists 6. Check `directoryInfo.displayInDirectory` setting **Resolution**: If user exists via API but not visible in console, check directory visibility settings per [Business Rules - Directory Visibility](#directory-visibility). ### Missing User Data After Creation **Problem**: User created but missing expected optional fields **Debugging Steps**: 1. Verify operation completed successfully (status: COMPLETED) 2. Check original request body for missing optional fields 3. Retrieve user with GET to see actual stored data 4. Review API response for warnings or partial success indicators 5. Check if downstream provisioning is delayed (extensions, licenses) **Resolution**: Optional fields not included in POST request are not populated. Submit new request with complete data or update user (when PUT is available). ### Site Reassignment Not Allowed **Problem**: Attempting to change user's site via PUT fails **Cause**: Site is immutable per [Business Rules - Site Assignment Constraints](#site-assignment-constraints) **Resolution**: Follow the three-step workaround: 1. Document complete user configuration (GET and save) 2. Delete user (DELETE operation) 3. Recreate with new site ID (POST with same data, different site) 4. Update external system references (user ID will change) ### Duplicate Username on Bulk Import **Problem**: Some users fail during bulk import with duplicate username **Cause**: Username conflicts with existing users or within import batch **Resolution**: - Pre-check usernames with GET `/users?filter=basicInfo.userName=={username}` - Implement numbering scheme (jane.smith, jane.smith2, jane.smith3) - Validate import data for internal duplicates before submission - Handle failures gracefully with detailed error logging --- ## Additional Resources **API Documentation**: - [Administration API Essentials](./suite-common.mdx) - [OpenAPI Specification](/administration/user-api-v1.yaml) - [8x8 Admin Console](https://admin.8x8.com) **Technical References**: - [RSQL Specification](https://github.com/jirutka/rsql-parser) (for filtering syntax) - [RFC 7807 - Problem Details](https://tools.ietf.org/html/rfc7807) (error format) - [ISO 8601 Date Format](https://en.wikipedia.org/wiki/ISO_8601) - [E.164 Phone Number Format](https://en.wikipedia.org/wiki/E.164) **Service Status**: - [8x8 Status Page](https://status.8x8.com) **Support**: - Admin Console: User profile menu → Contact Support **When Contacting Support**: See [API Essentials - Support Resources](./suite-common.mdx#support-resources) for required information. --- **API Version**: 1.0 | **Last Updated**: January 15, 2026 | **Part of**: [Administration API Suite](./suite-common.mdx) | **Feedback**: Submit feedback via Admin Console --- ## 8x8 Administration - Address Management API import Heading from "@theme/Heading"; 8x8 Administration - Address Management API Address management API providing endpoints to create, retrieve, list, and delete addresses. The current version of the API is v1.0. ## Authentication All requests to this API require authentication using an API key. Include your API key in the request header: ``` x-api-key: YOUR_API_KEY ``` ## Versioning The API version is specified through a vendor-specific media type. Send it in the `Content-Type` header for requests that carry a payload (such as `POST`) and in the `Accept` header for requests that return a payload (`GET`). The synchronous `DELETE` endpoint returns no content (`204`) and is not versioned. ``` Content-Type: application/vnd.addresses.v1+json # on POST (request payload) Accept: application/vnd.addresses.v1+json # on GET (response payload) ``` ## Base URL `https://api.8x8.com/admin-provisioning` ## Endpoints | Endpoint | Method | Purpose | Async? | |----------|--------|---------|--------| | `/addresses` | GET | Retrieve paginated list of addresses with filtering | No | | `/addresses` | POST | Create a new address | No | | `/addresses/{addressId}` | GET | Retrieve a specific address's details | No | | `/addresses/{addressId}` | DELETE | Remove an address | No | ## OpenAPI Specification Download the complete OpenAPI specification: [address-api-v1.yaml](/administration/address-api-v1.yaml) --- ## 8x8 Administration - Operations API import Heading from "@theme/Heading"; 8x8 Administration - Operations API Operation management API providing endpoints to retrieve and track the status of asynchronous operations. Operations are created automatically when asynchronous resource operations (create, update, delete) are initiated. The current version of the API is v1.0. ## Authentication All requests to this API require authentication using an API key. Include your API key in the request header: ``` x-api-key: YOUR_API_KEY ``` ## Versioning Specify the API version using the `Accept` header: ``` Accept: application/vnd.operations.v1+json ``` ## Base URL `https://api.8x8.com/admin-provisioning` ## Endpoints | Endpoint | Method | Purpose | Async? | |----------|--------|---------|--------| | `/operations/{operationId}` | GET | Retrieve the status and details of an asynchronous operation | No | ## OpenAPI Specification Download the complete OpenAPI specification: [operation-api-v1.yaml](/administration/operation-api-v1.yaml) --- ## 8x8 Administration - Phone Number Management API import Heading from "@theme/Heading"; 8x8 Administration - Phone Number Management API Phone number management API providing endpoints to list and retrieve phone numbers. The current version of the API is v1.0. ## Authentication All requests to this API require authentication using an API key. Include your API key in the request header: ``` x-api-key: YOUR_API_KEY ``` ## Versioning Specify the API version using the `Accept` header: ``` Accept: application/vnd.phonenumbers.v1+json ``` ## Base URL `https://api.8x8.com/admin-provisioning` ## Endpoints | Endpoint | Method | Purpose | Async? | |----------|--------|---------|--------| | `/phone-numbers` | GET | Retrieve paginated list of phone numbers with filtering | No | | `/phone-numbers/{phoneNumber}` | GET | Retrieve a specific phone number's details | No | ## OpenAPI Specification Download the complete OpenAPI specification: [phonenumber-api-v1.yaml](/administration/phonenumber-api-v1.yaml) --- ## 8x8 Administration - Ring Group Management API import Heading from "@theme/Heading"; 8x8 Administration - Ring Group Management API Ring group management API providing endpoints to create, retrieve, list, update, and delete ring groups. The current version of the API is v1.0. ## Authentication All requests to this API require authentication using an API key. Include your API key in the request header: ``` x-api-key: YOUR_API_KEY ``` ## Versioning The API version is specified through a vendor-specific media type. Send it in the `Content-Type` header for requests that carry a payload (such as `POST` and `PUT`) and in the `Accept` header for requests that return a payload (`GET`, and asynchronous `DELETE` operations that return an operation resource). ``` Content-Type: application/vnd.ringgroups.v1+json # on POST/PUT (request payload) Accept: application/vnd.ringgroups.v1+json # on GET (response payload) ``` ## Base URL `https://api.8x8.com/admin-provisioning` ## Endpoints | Endpoint | Method | Purpose | Async? | |----------|--------|---------|--------| | `/ring-groups` | GET | Retrieve paginated list of ring groups with filtering | No | | `/ring-groups` | POST | Create a new ring group | Yes | | `/ring-groups/{ringGroupId}` | GET | Retrieve a specific ring group's details | No | | `/ring-groups/{ringGroupId}` | PUT | Update an existing ring group | Yes | | `/ring-groups/{ringGroupId}` | DELETE | Remove a ring group | Yes | | `/ring-groups/{ringGroupId}/update-members` | POST | Atomically add, update, or remove ring group members | Yes | ## OpenAPI Specification Download the complete OpenAPI specification: [ringgroup-api-v1.yaml](/administration/ringgroup-api-v1.yaml) --- ## 8x8 Administration - Site Management API import Heading from "@theme/Heading"; 8x8 Administration - Site Management API Site management API providing endpoints to create, retrieve, list, update, and delete sites. Sites represent physical or logical locations within an organization where users and devices are deployed. The current version of the API is v1.0. ## Authentication All requests to this API require authentication using an API key. Include your API key in the request header: ``` x-api-key: YOUR_API_KEY ``` ## Versioning The API version is specified through a vendor-specific media type. Send it in the `Content-Type` header for requests that carry a payload (such as `POST` and `PUT`) and in the `Accept` header for requests that return a payload (`GET`, and asynchronous `DELETE` operations that return an operation resource). ``` Content-Type: application/vnd.sites.v1+json # on POST/PUT (request payload) Accept: application/vnd.sites.v1+json # on GET (response payload) ``` ## Base URL `https://api.8x8.com/admin-provisioning` ## Endpoints | Endpoint | Method | Purpose | Async? | |----------|--------|---------|--------| | `/sites` | GET | Retrieve paginated list of sites with filtering | No | | `/sites` | POST | Create a new site | Yes | | `/sites/{siteId}` | GET | Retrieve a specific site's details | No | | `/sites/{siteId}` | PUT | Update an existing site | Yes | | `/sites/{siteId}` | DELETE | Remove a site | Yes | ## OpenAPI Specification Download the complete OpenAPI specification: [site-api-v1.yaml](/administration/site-api-v1.yaml) --- ## 8x8 Administration - User Management API import Heading from "@theme/Heading"; 8x8 Administration - User Management API User management API providing endpoints to create, retrieve, list, update, and delete users. The current version of the API is v1.0. ## Authentication All requests to this API require authentication using an API key. Include your API key in the request header: ``` x-api-key: YOUR_API_KEY ``` ## Versioning The API version is specified through a vendor-specific media type. Send it in the `Content-Type` header for requests that carry a payload (such as `POST` and `PUT`) and in the `Accept` header for requests that return a payload (`GET`, and asynchronous `DELETE` operations that return an operation resource). ``` Content-Type: application/vnd.users.v1+json # on POST/PUT (request payload) Accept: application/vnd.users.v1+json # on GET (response payload) ``` ## Base URL `https://api.8x8.com/admin-provisioning` ## Endpoints | Endpoint | Method | Purpose | Async? | |----------|--------|---------|--------| | `/users` | GET | Retrieve paginated list of users with filtering | No | | `/users` | POST | Create a new user account | Yes | | `/users/{userId}` | GET | Retrieve a specific user's details | No | | `/users/{userId}` | PUT | Update an existing user account | Yes | | `/users/{userId}` | DELETE | Remove a user account | Yes | ## OpenAPI Specification Download the complete OpenAPI specification: [user-api-v1.yaml](/administration/user-api-v1.yaml) --- ## Create address import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Create a new address --- ## Create a ring group import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Creates a new ring group asynchronously. Returns an operation object to track the creation progress. --- ## Create a new site import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Creates a new site. Returns an Operation object that can be polled to track the creation progress. --- ## Create user import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Create a new user. This operation is asynchronous and returns an Operation object to track progress. --- ## Delete address import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Delete a specific address by its ID --- ## Delete a ring group import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Deletes an existing ring group asynchronously. Returns an operation object to track the deletion progress. --- ## Delete site import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Deletes an existing site. Returns an Operation object that can be polled to track the deletion progress. --- ## Delete user import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Delete a specific user by their ID. This operation is asynchronous and returns an Operation object to track progress. --- ## Get address by ID import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieve a specific address by its ID --- ## Get operation by ID import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the current status and details of an asynchronous operation. Operations are used to track the progress of resource creation, updates, and deletions. The operation status is polled from the underlying service and updated in real-time. --- ## Get phone number by ID import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieve a specific phone number by its value. The phone number must be in E.164 format (e.g., +14085551234). --- ## Get ring group by ID import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves a specific ring group by its unique identifier. --- ## Get site by ID import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieve a specific site by its ID --- ## Get user by ID import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieve a specific user by their ID --- ## List addresses import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; List addresses with optional filtering, sorting, and pagination. Uses infinite scroll pagination with scrollId for efficient navigation. --- ## List phone numbers import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; List phone numbers with optional filtering, sorting, and infinite scroll pagination. Uses scrollId-based pagination for efficient navigation through large datasets. Phone numbers must be in E.164 format (e.g., +14085551234). --- ## List ring groups import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; List ring groups with optional filtering, sorting, and pagination. Uses infinite scroll pagination with scrollId for efficient navigation. (greater than), < (less than), >= (greater or equal), <= (less or equal). Use ; for AND, , for OR. Wildcard matching with * is supported for string fields only. Example: name==*Reception*","required":false,"schema":{"type":"string","maxLength":2000}},{"name":"sort","in":"query","description":"Sort expression. Use '+' prefix or no prefix for ascending order, '-' prefix for descending order (e.g., 'name', '+name', or '-name')","required":false,"schema":{"type":"string","maxLength":200}},{"name":"scrollId","in":"query","description":"Scroll ID for fetching the next page of results. Obtained from the previous page response.","required":false,"schema":{"type":"string"}}]} > --- ## List sites import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; List sites with optional filtering, sorting, and pagination. Filter by site name using RSQL expressions with wildcards. --- ## List users import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; List users with optional filtering, sorting, and infinite scroll pagination. Uses scrollId-based pagination for efficient navigation through large datasets. --- ## Update ring group members import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Atomically applies an add/update/remove delta to a ring group's member list. Returns an operation object to track progress; poll the operation to completion. Only the members supplied are changed. At least one of `add`, `update`, or `remove` must be non-empty, each accepts up to 200 members, and an `extensionId` or `extensionNumber` must not appear in more than one array within a single request. --- ## Update a ring group import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Updates an existing ring group asynchronously. Returns an operation object to track the update progress. --- ## Update site import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Updates an existing site. Returns an Operation object that can be polled to track the update progress. --- ## Update user import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Update an existing user by their ID. This operation is asynchronous and returns an Operation object to track progress. --- ## Audit Records ## Overview As an administrator, you may need to access the audits across some 8x8 services. Currently, administrator can retrieve audit records of following services: * **platform** - **create**, **delete** and **update** events in platform service. The Audit records API currently supports following HTTP methods: * **GET** is supported to retrieve audit records. ### 1. Get Audits This GET method paginates the audit records using scrollId. It provides an option to filter based on given query parameters. The sorting by attributes is not supported. #### Base URL * [https://api.8x8.com/administration/audit/v1/audits](https://api.8x8.com/administration/audit/v1/audits) #### Parameters **Method: GET** #### Headers | Name | Required | Description | Example | | ------------ | -------- | ---------------------------------------------------------------------------------------- | ----------------------------- | | x-api-key | ✓ | API Credential Key from the [Admin Console Process](/analytics/docs/how-to-get-api-keys) | eght_Abcdhfakdlbdfsjkbskzkmxl | | content-type | ✓ | application/json | application/json | #### Query | Name | Required | Description | Example | | ----------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | | startTime | ✓ | Start time for audit records (ISO 8601 format), it should be a maximum of 31 days before end time. | 2022-12-16T18:30:00.000Z | | endTime | ✓ | End time of audit records (ISO 8601 format). | 2023-01-16T18:30:00.000Z | | service | ✓ | Name of a service that generated audit records. | platform | | displayName | ☐ | Name linked with entity i.e, if an entity is user it's userName, if an entity is RingGroup it is a ring group name. It also allows search using starts with(\*abc) and ends with(abc\*) wildcards. | \*Ring Group\* | | eventTypes | ☐ | Comma-separated strings of event types. | create, delete, view | | entityTypes | ☐ | Comma-separated strings of entity types. | user, ringgroup, extension | | auditUserId | ☐ | The identifier of a user who performed an audits action. | HTHiKjDEFfK7X03ABCj_IQ | | size | ☐ | Maximum number of items per page. It must be greater than zero. Default value is 20, maximum is 100. | 1 | | scrollId | ☐ | The scrollId parameter returned from your previous call. You can include this parameter in your next or subsequent calls to retrieve the next page of records. To retrieve the first page no need to provide scrollId. | 012345677-89abb-cdef-0123-456789abcdef | #### Full Request Example ```bash curl --location 'https://api.8x8.com/administration/audit/v1/audits? size=1& startTime=2023-05-01T18:30:00.000& endTime=2023-05-31T23:59:59.651& service=platform& displayName=*Bes*& auditUserId=DctVyxEFR86EYyyDFoSLNA& entityTypes=RingGROUP, Agentgroup& eventTypes=create, update& scrollId=6bca6d01-774c-4115-8a03-eff95f430b06' \ --header 'x-api-key:eght_etk_039jfadf98j3f9a8jfa098fj3' \ --header 'Accept: application/json' ``` > 📘 **'scrollId' In Request** > > Above request retrieves page with scrollId in query parameter, if you want to get the first page don’t provide scrollId. > > #### Response ```json { "meta": { "totalRecordCount": 1, "scrollId": "1fc519a4-2008-4234-b720-9cfdaf8866e6" }, "data": [ { "id": "1fc519a4-2008-4234-b720-9cfdaf8866e6", "displayName": "test_bes", "customerId": "bes-tests-functional1", "auditTimestamp": "2023-05-02T20:57:34.956+00:00", "eventType": "create", "service": "platform", "entityType": "AgentGroup", "entityKey": "100", "auditUserId": "UgDHZNAZTduIVLE5lkjOkg", "impersonator": null, "details": "{\"id":41, \"name\":\"ungroup\", \"agent_count\":13 }", "correlationType": null, "correlationId": null } ] } ``` > 📘 **'scrollId' In Response** > > When a user gets **scrollId null in response** it would mean there is **no more data to retrieve** for a given request. Thus, the **scrollId value equal to null in response** implies the **last page**. > > #### Response body fields description | Name | Description | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | displayName | The human readable form of the entityKey (where applicable). E.g., If the entityType is “user”, this will contain the user’s first and last name. | | auditTimestamp | The date/time (in UTC) when the event was registered. | | eventType | Specifies how the event changed/accessed the entity: create, update, delete, view, export. | | service | Specifies which 8x8 service was involved in the event. At time of writing, only “platform” is available; “platform” refers to changes made in the 8x8 Admin Console. | | entityKey | The identifier of the entity that was changed/accessed in this audit event. | | entityType | Specifies the type of entity that was changed/accessed in this audit event. E.g., user, extension, call forwarding. | | auditUserId | The identifier of the user who made the change (or accessed the record). | | Impersonator | In rare cases where 8x8 agents make changes on behalf of customers (e.g., as a result of a support request), the impersonator column will contain the identifier of the user who made the change. | | details | Contains the detailed information on which attributes of the changed entity were updated. This field is in JSON representation: {"new":{"changedAttributeName":"new value","newAttributeName":"added value"},"old":{"changedAttributeName":"old value","deletedAttributeName":"deleted value"}}The “new” object contains all of the newly-added attributes and the new value of changed attributesThe “old” object contains all of the deleted attributes and the old value of changed attributes. | | correlationType | When several changes are made to various entities as part of one change set, the correlationType displays the parent entity type. E.g., if an extension was assigned to a user during a user creation flow, the entityType would be “extension” and the correlationType would be “user”. | | correlationId | The identifier of the parent entity of this change. | --- ## CC Historical Analytics Detailed Report ## Approach for JSON vs XLSX/CSV The common `historical-metrics` endpoint has a limitation where the JSON `/data` response is limited to 10,000 records. The XLSX/CSV download available at `historical-metrics/detailed-report` does not have this limitation. To overcome this limitation the `historical-metrics/detailed-report` endpoint is available to allow for consumers to access JSON format data for large result sets expected with detailed reports. > ❗️ **Requirement: for CSV/XLSX content** > > Leverage the `historical-metrics` endpoint. CSV/XLSX is not available via `historical-metrics/detailed-report` > > > ❗️**Strong Recommendation: for JSON content** > > Leverage the `historical-metrics/detailed-report` endpoint and the result set won't be limited to 10,000 records. > > This guarantees a full result set regardless of the size of the response. > > > 📘 **You will need a working API key to begin** > > [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > The base URL is region specific, based on the location of your Contact Center tenant. * United States: `https://api.8x8.com/analytics/cc/{version}/historical-metrics/` * Europe: `https://api.8x8.com/eu/analytics/cc/{version}/historical-metrics/` * Asia-Pacific: `https://api.8x8.com/au/analytics/cc/{version}/historical-metrics/` * Canada: `https://api.8x8.com/ca/analytics/cc/{version}/historical-metrics/` * {version} to be replaced by current Version. As of August 2025 this is 8 resulting in /v8/ ## 1. Authenticate to retrieve access token [OAuth Authentication for 8x8 XCaaS APIs](/analytics/docs/oauth-authentication-for-8x8-xcaas-apis) is used to get a temporary `access_token` for use in with this API **Outputs For Next Step:** * access_token * expires_in The following steps will use the access_token as a Bearer Token form of authentication. This takes the form of the `Authorization` header being set to `Bearer access_token` (Space between Bearer and the access_token) ## 2 Multitenancy support If the API is used for a multitenant customer the requests should contain *"X-Tenant-Info"* header variable where needs to specify the desired tenantId. The "X-Tenant-Info" header is not mandatory in case of a single tenant customer. The following error messages could be returned when dealing with a multitenant customer: * if for a multitenant customer request the *"X-Tenant-Info"* header is not provided the HTTP 400 code along with *"Bad request: X-Tenant-Info header is missing."* message will be returned * if a wrong tenantId is provided the HTTP 400 code along with *"Bad request: Invalid value for X-Tenant-Info header."* message will be returned ## 3. Get Available Report Types CC Historical Analytics allows the consumer to get a listing of the available reports including information about their options and available data. > 📘 **Available Detailed Reports** > > Currently `detailed-reports-interaction-details` and `detailed-reports-agent-status-change` are the available detailed interaction reports. For detailed Post-call survey reports, see the [Post Call Survey](/analytics/docs/customer-experience-post-call-survey) section. > > Additional information about each of the reports and detailed definitions of metrics can be found in the [Interaction Details Report Metrics Glossary](#31-interaction-details-report-metrics-glossary) and [Agent Status Change Detailed Report Metrics Glossary](#32-agent-status-change-detailed-report-metrics-glossary) ### Parameters **Method: GET** #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version is `v8` | v8 | | report-type | ☐ | Specific report type to get information on. Omit this parameter to get all report types. | agent-status-by-status-code | [API reference](/analytics/reference/cc-historical-analytics-report-types) ### Report Types Request The response shows each `report-type` that's available. all report typessingle report type ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v8/historical-metrics/report-types' \ --header 'Authorization: Bearer {access_token}' ``` ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v8/historical-metrics/report-types/detailed-reports-interaction-details' \ --header 'Authorization: Bearer {access_token}' ``` ### Report Types Response The response shows each `report-type` that's available. **Outputs For Next Step:** For detailed reports the response has a number of elements to guide the usage: * `type` each report type has a unique definition * `metrics` these are the available metrics for the report type. When creating a report. See the [Interaction Details Report Metrics Glossary](#31-interaction-details-report-metrics-glossary) and [Agent Status Change Detailed Report Metrics Glossary](#32-agent-status-change-detailed-report-metrics-glossary) for additional detail on the definition of the available metrics * if no metrics are specified: All metrics will be returned * if metrics are specified: ONLY the specified metrics will be returned * `searchQuery` Provides information on the searchable fields and the operators for those searches. See [searchQuery](/analytics/docs/cc-historical-analytics-detailed-report#searchquery) below for more detailed description. * `fields` field name of searchable field * `operators` list of valid operators Generic Example: ```json [ { "type": "report type name", "metrics": [ "report metric 1", "report metric 2" ], "searchQuery": { "fields": [ "searchable field 1", "searchable field 2" ], "operators": [ "=", "!=", ">", ">=", "<=", "<", "contains", "not-in", "in", "is-empty", "is-not-empty" ] } } ] ``` **Sample Response for single report type** ```json { "type": "detailed-reports-interaction-details", "metrics": [ "agentNotes", "blindTransferToAgent", "blindTransferToQueue", "campaignId", "campaignName", "caseFollowUp", "caseNumber", "channelId", "conferencesEstablished", "consultationsEstablished", "creationTime", "customerName", "destination", "direction", "dispositionAction", "externalTransactionData", "finishedTime", "interactionId", "interactionLabels", "interactionType", "ivrTreatmentDuration", "mediaType", "originalInteractionId", "originalTransactionId", "origination", "outboundPhoneCode", "outboundPhoneCodeId", "outboundPhoneCodeList", "outboundPhoneCodeListId", "outboundPhoneCodeText", "outboundPhoneShortCode", "participantAssignNumber", "participantBusyDuration", "participantHandlingDuration", "participantHandlingEndTime", "participantHold", "participantHoldDuration", "participantId", "participantLongestHoldDuration", "participantName", "participantOfferAction", "participantOfferActionTime", "participantOfferDuration", "participantOfferTime", "participantProcessingDuration", "participantType", "participantWrapUpDuration", "participantWrapUpEndTime", "queueId", "queueName", "queueTime", "queueWaitDuration", "recordId", "time", "transactionId", "warmTransfersCompleted", "wrapUpCode", "wrapUpCodeId", "wrapUpCodeList", "wrapUpCodeListId", "wrapUpCodeText", "wrapUpShortCode" ], "searchQuery": { "fields": [ "agentNotes", "blindTransferToAgent", "blindTransferToQueue", "campaignId", "campaignName", "caseFollowUp", "caseNumber", "channelId", "conferencesEstablished", "consultationsEstablished", "creationTime", "customerName", "destination", "direction", "dispositionAction", "externalTransactionData", "finishedTime", "interactionId", "interactionLabels", "interactionType", "ivrTreatmentDuration", "mediaType", "originalInteractionId", "originalTransactionId", "origination", "outboundPhoneCode", "outboundPhoneCodeId", "outboundPhoneCodeList", "outboundPhoneCodeListId", "outboundPhoneCodeText", "outboundPhoneShortCode", "participantAssignNumber", "participantBusyDuration", "participantHandlingDuration", "participantHandlingEndTime", "participantHold", "participantHoldDuration", "participantId", "participantLongestHoldDuration", "participantName", "participantOfferAction", "participantOfferActionTime", "participantOfferDuration", "participantOfferTime", "participantProcessingDuration", "participantType", "participantWrapUpDuration", "participantWrapUpEndTime", "queueId", "queueName", "queueTime", "queueWaitDuration", "recordId", "time", "transactionId", "warmTransfersCompleted", "wrapUpCode", "wrapUpCodeId", "wrapUpCodeList", "wrapUpCodeListId", "wrapUpCodeText", "wrapUpShortCode" ], "operators": [ "=", "!=", ">", ">=", "<=", "<", "contains", "not-in", "in", "is-empty", "is-not-empty" ] } } ``` ## 3.1. Interaction Details Report Metrics Glossary This glossary provides comprehensive definitions for all metrics available in the `detailed-reports-interaction-details` report type. Use this reference when selecting metrics for your reports and understanding the data returned.
Click to expand Metrics Glossary (95 metrics) **Version** indicates minimum CC Historical Analytics API version where metric became available. | Field | Version | Description | Allowed Filter Operators | Predefined Filter Values | |----------------------------------|---------|-------------|----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `agentNotes` | v3+ | Free-text notes added by the agent on the interaction for reference and quality purposes. These notes can be used for documentation, follow-up actions, or quality assurance. | in, not-in, contains, is-empty, is-not-empty | | | `blindTransferToAgent` | v3+ | Total blind transfers initiated and received by an agent outside queues | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `blindTransferToQueue` | v3+ | Total blind transfers to queues initiated by an agent | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `campaignId` | v3+ | Unique identifier of the outbound campaign associated with this interaction. This field is populated when the interaction originates from an outbound campaign. | in, not-in, contains, is-empty, is-not-empty | | | `campaignName` | v3+ | Human-readable name of the outbound campaign associated with this interaction. This field is populated when the interaction originates from an outbound campaign. | in, not-in, contains, is-empty, is-not-empty | | | `caseFollowUp` | v3+ | The CRM case follow-up associated with the interaction | in, not-in, contains, is-empty, is-not-empty | | | `caseNumber` | v3+ | The CRM case associated with the interaction | in, not-in, contains, is-empty, is-not-empty | | | `channelId` | v3+ | The channel through which an incoming interaction was directed. | in, not-in, contains, is-empty, is-not-empty | | | `channelName` | v7+ | A unique name for the channel that allows you to easily identify the location of an interaction. | in, not-in, contains, is-empty, is-not-empty | | | `chatType` | v6+ | The specific digital channel platform used for chat-based interactions. Possible values:• **ChatApi**: Standard 8x8 Chat API integration• **RCS**: Rich Communication Services messaging• **SMS**: Short Message Service text messaging• **Facebook**: Facebook Messenger• **Twitter**: Twitter Direct Messages• **WebChat**: Browser-based web chat• **Viber**: Viber messaging platform• **WhatsApp**: WhatsApp messaging platform | in, not-in, contains, is-empty, is-not-empty | chatApi, webChat, facebook, twitter, rcs, sms, whatsApp, viber | | `conferencesEstablished` | v3+ | Total number of conferences established by the agent | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `consultationsEstablished` | v3+ | Times an agent successfully established an outbound call while another call is on hold | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `creationTime` | v3+ | Timestamp when the interaction was created | Not filterable | | | `customerEmail` | v8+ | Email address of the customer involved in the interaction | in, not-in, contains, is-empty, is-not-empty | | | `customerJourneyDuration` | v3+ | Time duration a customer spent on an interaction including script time, queue time, handling time, hold time, and post-call survey time (when applicable) | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `customerName` | v3+ | The name of the customer, if available | in, not-in, contains, is-empty, is-not-empty | | | `customerPhone` | v8+ | Phone number of the customer involved in the interaction | in, not-in, contains, is-empty, is-not-empty | | | `destination` | v3+ | The point where the interaction landed. | in, not-in, contains, is-empty, is-not-empty | | | `destinationQueueId` | v8+ | The unique ID of the last queue an interaction was routed to, even if the interaction didn't wait in that queue. | in, not-in, contains, is-empty, is-not-empty | | | `destinationQueueName` | v8+ | The name of the last queue an interaction was routed to, even if the interaction didn't wait there. | in, not-in, contains, is-empty, is-not-empty | | | `direction` | v3+ | Indicates whether the interaction was initiated by the customer or the agent. Possible values:• **InboundDir**: Customer-initiated interaction• **OutboundDir**: Agent-initiated interactionFor phone calls, this indicates the call direction. For chats, this reflects who started the conversation. For emails, this shows whether it was an incoming customer email or an outgoing agent email. | in, not-in, contains, is-empty, is-not-empty | inbound, outbound | | `dispositionAction` | v3+ | Automated action assigned to the interaction that determines how subsequent contact attempts are handled in campaigns. Possible values:• **NoCode**: No automated action applies• **TryAgain**: Automatically retry contact per campaign settings• **ScheduleCallback**: Schedule callback with agent• **DoNotCall**: Mark record as non-contactable | in, not-in, contains, is-empty, is-not-empty | noCode, tryAgain, scheduleCallback, doNotCall | | `externalTransactionData` | v3+ | Custom data passed from external systems or CRM integrations associated with the interaction. This field contains integration-specific information that can be used for reporting and analysis. | in, not-in, contains, is-empty, is-not-empty | | | `facebookId` | v7+ | Unique Facebook identifier associated with a customer interaction, if available | in, not-in, contains, is-empty, is-not-empty | | | `finishedTime` | v3+ | Timestamp when the interaction was finished | Not filterable | | | `interactionDuration` | v3+ | Total time spent in script, queue, and handling. Does not include wrap-up time. | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `interactionId` | v3+ | Contact Center's internal interaction ID | in, not-in, contains, is-empty, is-not-empty | | | `interactionLabels` | v3+ | A list of labels that represent important events that took place in the interaction; see below for possible values:• **Queued**: Interaction placed into a queue awaiting an available agent• **Dequeued**: Previously queued interaction was removed from the queue• **Abandoned**: Customer ended the interaction while waiting in queue before reaching an agent• **TransferredToAgent**: Interaction was routed to a specific agent through agent routing• **Rejected**: Agent declined the interaction when it was offered• **OfferingTimeout**: Agent did not respond to the interaction offer within the timeout period• **Handled**: Agent accepted and processed the interaction• **TransferredToQueue**: Interaction was transferred to another queue by the agent• **Deleted**: Interaction was removed from the system• **ForwardedToQueue**: Script forwarded the interaction to a queue• **ForwardedToVoicemail**: Script directed the interaction to a voicemail system• **ForwardedToCallback**: Interaction was scheduled for a callback• **EndedInIvr**: Customer disconnected while in the IVR before reaching queue, agent, or voicemail• **ConsultationEstablished**: Agent initiated an internal consultation call with another agent• **UnknownOutcome**: System could not determine the final outcome of the interaction• **Canceled**: Scheduled callback expired without agent availability or was cancelled• **ForwardedToExternalNumber**: Interaction was routed to an external phone number• **OutboundSmsSent**: Outbound SMS or RCS message was successfully sent• **VideoSmsSent**: Video SMS or RCS message was successfully sent• **RepliedToEmail**: Agent sent a response to the customer's email• **PostCallSurveyCallback**: Post-call survey was scheduled as a callback interaction• **PostCallSurveyStayOnCall**: Customer remained on the call to complete the post-call survey• **ForwardedToExternalIvr**: Interaction was routed to an external IVR system• **ForwardedToAgentIndividualVoicemail**: Interaction was directed to a specific agent's personal voicemail• **BlindTransfer**: Interaction was transferred directly without agent consultation• **TransferToAnotherNumber**: Interaction was redirected to a different phone number• **DirectAgentAccess**: Customer reached an agent directly without going through a queue• **DirectAgentRouting**: Interaction was routed directly to a specific agent• **ConferenceEstablished**: Multi-party conference call was created | in, not-in, contains, is-empty, is-not-empty | Queued, Dequeued, Abandoned, TransferredToAgent, Rejected, OfferingTimeout, Handled, TransferredToQueue, Deleted, ForwardedToQueue, ForwardedToVoicemail, ForwardedToCallback, EndedInIvr, ConsultationEstablished, UnknownOutcome, Canceled, ForwardedToExternalNumber, OutboundSmsSent, VideoSmsSent, RepliedToEmail, PostCallSurveyCallback, PostCallSurveyStayOnCall, ForwardedToExternalIvr, ForwardedToAgentIndividualVoicemail, BlindTransfer, TransferToAnotherNumber, DirectAgentAccess, DirectAgentRouting, ConferenceEstablished | | `interactionType` | v3+ | Categorizes the interaction type for special scenarios. Empty for regular inbound interactions. Possible values:• **OutboundCall**: Outbound call made by agent manually, as opposed to other options (campaign, callback)• **Callback**: Inbound call selected for callback• **VerificationCall**: Verification call• **InternalCall**: Call between two internal users• **ConsultationCall**: Internal call made while agent was working on another interaction in busy state• **Campaign**: Campaign call• **PostCallSurveyCallback**: Callback for post-call survey | in, not-in, contains, is-empty, is-not-empty | OutboundCall, Callback, VerificationCall, InternalCall, ConsultationCall, Campaign, PostCallSurveyCallback | | `ivrTreatmentDuration` | v3+ | Total time caller spent in IVR, excluding queue time | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `journeyId` | v8+ | A unique ID that groups all segments of an interaction across transfers, representing the full customer journey. | in, not-in, contains, is-empty, is-not-empty | | | `mediaType` | v3+ | The communication channel type for the interaction. Possible values: Phone, Chat, Email, VoiceMail | in, not-in, contains, is-empty, is-not-empty | phone, chat, email, voiceMail | | `originalInteractionId` | v3+ | The interaction ID of the parent interaction | in, not-in, contains, is-empty, is-not-empty | | | `originalTransactionId` | v3+ | The transaction ID of the parent interaction | in, not-in, contains, is-empty, is-not-empty | | | `origination` | v3+ | The point where the interaction originated from | in, not-in, contains, is-empty, is-not-empty | | | `outboundPhoneCode` | v3+ | The Menu Name of the outbound phone code item chosen by the agent (optional, outbound only) | in, not-in, contains, is-empty, is-not-empty | | | `outboundPhoneCodeId` | v3+ | The ID of the outbound phone code item (optional) | in, not-in, contains, is-empty, is-not-empty | | | `outboundPhoneCodeList` | v3+ | The code list the code is part of (optional) | in, not-in, contains, is-empty, is-not-empty | | | `outboundPhoneCodeListId` | v3+ | The ID of the outbound phone code list the code is part of (optional) | in, not-in, contains, is-empty, is-not-empty | | | `outboundPhoneCodeText` | v3+ | The Report Text of the outbound phone code item (optional) | in, not-in, contains, is-empty, is-not-empty | | | `outboundPhoneShortCode` | v3+ | The Short Code of the outbound phone code item (optional) | in, not-in, contains, is-empty, is-not-empty | | | `outcome` | v3+ | The final outcome label of the interaction, indicating how it concluded. This represents the terminal state of the interaction from the system's perspective. Possible values are the same as those defined in the interactionLabels field above. | in, not-in, contains, is-empty, is-not-empty | Queued, Dequeued, Abandoned, TransferredToAgent, Rejected, OfferingTimeout, Handled, TransferredToQueue, Deleted, ForwardedToQueue, ForwardedToVoicemail, ForwardedToCallback, EndedInIvr, ConsultationEstablished, UnknownOutcome, Canceled, ForwardedToExternalNumber, OutboundSmsSent, VideoSmsSent, RepliedToEmail, PostCallSurveyCallback, PostCallSurveyStayOnCall, ForwardedToExternalIvr, ForwardedToAgentIndividualVoicemail, BlindTransfer, TransferToAnotherNumber, DirectAgentAccess, DirectAgentRouting, ConferenceEstablished | | `participantAssignNumber` | v3+ | An ordinal number for each agent assignment (first agent gets 1, second agent gets 2, and so on) | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantAssignReason` | v8+ | Reason the interaction was assigned to this participant. Possible values:• **Initiated**: Agent initiated an internal call or an outbound call• **Offered**: Standard queue assignment, direct agent call, callback, or campaign• **Parked**: Participant row corresponding to a park operation by this agent• **Unparked**: Agent received a previously parked interaction back• **ConferenceJoined**: Agent joined a conference call• **TransferReceived**: Agent received a blind or warm transfer | in, not-in, contains, is-empty, is-not-empty | Initiated, Offered, Parked, Unparked, ConferenceJoined, TransferReceived | | `participantAssignTime` | v8+ | Timestamp when the participant row was assigned to the participant. Similar to `participantOfferTime` but reflects the assignment moment, which differs for parked / unparked / transferred interactions | Not filterable | | | `participantBusyDuration` | v3+ | Combined time agents spent in Offering, Handling, and Wrap-up states | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantComments` | v8+ | Agent notes captured during park or wrap-up operations. Returned as a JSON array of `{timestamp, participantId, participantName, comment}` objects in the JSON API response, and as a JSON-encoded string in CSV/XLSX exports | in, not-in, contains, is-empty, is-not-empty | | | `participantFocus` | v8+ | Number of times an interaction was placed in focus state for a participant (agent). Tracks how many focus/unfocus transitions occurred during an interaction. Focus time represents the duration when agents are actively viewing and engaging with a chat or email interaction, as opposed to performing other tasks such as consulting knowledge bases or documentation. | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantFocusDuration` | v8+ | Total cumulative duration that a participant (agent) spent with the interaction in focus state. Focus time measures when agents are actively viewing and working on a chat or email interaction window, versus time spent on other activities like researching information or accessing external resources. | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantGroupId` | v3+ | The unique identifier of the specific group to which an agent belongs | in, not-in, contains, is-empty, is-not-empty | | | `participantGroupName` | v3+ | The name of the specific group to which an agent belongs | in, not-in, contains, is-empty, is-not-empty | | | `participantHandlingDuration` | v3+ | Time between Offer Action Time and Handling End Time, calculated individually per participant | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantHandlingEndTime` | v3+ | Timestamp for when the 'handling' phase of the interaction finished | Not filterable | | | `participantHandlingOutcome` | v8+ | How the participant's handling of the interaction concluded. Possible values:• **WrapUp**: Handling ended and was followed by wrap-up• **Finished**: Handling ended without wrap-up• **Parked**: Handling ended because the interaction was parked• **UnknownOutcome**: Outcome could not be determined | in, not-in, contains, is-empty, is-not-empty | WrapUp, Finished, Parked, UnknownOutcome | | `participantHold` | v3+ | Counter of times the agent placed a call on hold | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantHoldDuration` | v3+ | The total duration of time the call was put on hold | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantId` | v3+ | If the participant is an agent, then the agent ID; empty for external numbers | in, not-in, contains, is-empty, is-not-empty | | | `participantLongestHoldDuration` | v3+ | The longest continuous duration for which the call was put on hold | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantLongestFocusDuration` | v8+ | Longest single continuous focus period for a participant (agent) on a specific interaction. Represents the maximum uninterrupted duration when an agent remained actively engaged with the interaction window without switching away to other tasks. | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantLongestMuteDuration` | v3+ | The longest continuous mute period by any agent during the interaction. This identifies the maximum single mute duration. | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantMute` | v3+ | Count of times agents muted their audio during the interaction. This metric tracks how many times the mute function was activated. | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantMuteDuration` | v3+ | Total cumulative time agents spent on mute during the interaction. This represents the sum of all mute periods. | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantName` | v3+ | An agent name to whom the interaction was assigned, or a customer's display name | in, not-in, contains, is-empty, is-not-empty | | | `participantOfferAction` | v3+ | Action taken in response to an interaction offer. Multiple offer actions can occur if interaction is offered multiple times. Possible values:• **Accepted**: Agent accepted the offer• **Rejected**: Agent explicitly rejected the offer• **OfferTimeout**: Agent did not respond within timeout period• **Abandoned**: Customer hung up during offer while interaction was queued• **CustomerHangUp**: Customer hung up during offer while interaction was not queued | in, not-in, contains, is-empty, is-not-empty | accepted, rejected, offerTimeout, abandoned, customerHangUp | | `participantOfferActionTime` | v3+ | Timestamp for when the participant took the action | Not filterable | | | `participantOfferDuration` | v3+ | Duration between agent offer time and agent action time | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantOfferTime` | v3+ | Timestamp for when the interaction was offered to the participant | Not filterable | | | `participantParkDuration` | v8+ | Total cumulative time the interaction was parked by this participant. Sum of all park periods for this participant row. Parked duration is NOT included in `participantHandlingDuration` | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantPark` | v8+ | Number of times this participant parked the interaction. Multiple park / unpark cycles within the same participant assignment increment this counter | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantProcessingDuration` | v3+ | Combined time spent by agents in Handling and Wrap-up states | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantType` | v3+ | The type of participant in the interaction. Possible values: Agent, ExternalNumber | in, not-in, contains, is-empty, is-not-empty | agent, externalNumber | | `participantWrapUpDuration` | v3+ | Duration between the start-post-processing and end-post-processing times | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `participantWrapUpEndTime` | v3+ | Timestamp for when the agent finished wrap-up | Not filterable | | | `queueId` | v3+ | A unique identifier for the queue. This is an optional column. | in, not-in, contains, is-empty, is-not-empty | | | `queueName` | v3+ | The name of the queue through which the interaction was directed. This is an optional column. | in, not-in, contains, is-empty, is-not-empty | | | `queueTime` | v3+ | The exact timestamp when the interaction entered the queue. This marks the moment when the interaction became available for agent assignment. | Not filterable | | | `queueWaitDuration` | v3+ | Time spent waiting in queue between queued and first accepted events | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `recordId` | v3+ | Identifier for the record associated with a campaign. This field is populated when the interaction originates from an outbound campaign. | in, not-in, contains, is-empty, is-not-empty | | | `recordingFileNames` | v3+ | List of all recording file names associated with this interaction. Multiple recordings are comma-separated. This field helps locate and access recorded media for quality assurance and compliance. | in, not-in, contains, is-empty, is-not-empty | | | `scheduleHours` | v3+ | Indicates whether the interaction occurred during scheduled business hours or outside hours. Possible values:• **Open**: Interaction occurred during scheduled business hours• **Closed**: Interaction occurred outside scheduled business hours | in, not-in, contains, is-empty, is-not-empty | Open, Closed | | `state` | v3+ | Current processing state of the interaction within the system workflow. This indicates where the interaction is in its lifecycle. Possible values:• **InScript**: Interaction is being processed through a Script• **WaitingInQueue**: Interaction is queued waiting for an available agent• **Handling**: Interaction is actively being handled by an agent• **Finished**: Interaction has been completed• **TransferredToExternalNumber**: Interaction was transferred to another phone number• **InWrapUp**: Agent is completing post-interaction wrap-up activities• **WaitingForInitiator**: Interaction is waiting for the initiating party to respond• **DelayedCallback**: Interaction is scheduled for a future callback | in, not-in, contains, is-empty, is-not-empty | InScript, WaitingInQueue, WaitingForInitiator, DelayedCallback, Handling, TransferredToExternalNumber, InWrapUp, Finished | | `subject` | v8+ | The subject of the first email in an email interaction | in, not-in, contains, is-empty, is-not-empty | | | `terminatedBy` | v3+ | Indicates which party initiated the termination of the interaction. Possible values:• **Agent**: The agent ended the interaction• **Customer**: The customer ended the interaction• **System**: The system automatically terminated the interaction | in, not-in, contains, is-empty, is-not-empty | | | `time` | v3+ | The date and time stamp of the creation of the interaction. | Not filterable | | | `timeToAbandon` | v3+ | Duration between the interaction entering the queue and the customer abandoning before agent connection. This metric only applies to abandoned interactions and measures how long the customer waited in queue before disconnecting. | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `transactionId` | v3+ | Also known as the "Token ID" or just "Transaction" in the old reports | in, not-in, contains, is-empty, is-not-empty | | | `transfers` | v3+ | List of transfer types performed during the interaction lifecycle. Multiple transfer types are comma-separated. Possible values:• **BlindTransfer**: Call was transferred without agent consultation• **TransferToAnotherNumber**: Call was transferred to an external phone number• **TransferToAgent**: Call was transferred directly to another agent• **TransferToQueue**: Call was transferred to another queue | in, not-in, contains, is-empty, is-not-empty | BlindTransfer, TransferToAnotherNumber, TransferToAgent, TransferToQueue | | `twitterId` | v7+ | Unique Twitter identifier associated with a customer interaction, if available | in, not-in, contains, is-empty, is-not-empty | | | `warmTransfersCompleted` | v3+ | Total number of warm transfers done during an interaction | =, !=, >, >=, <, <=, is-empty, is-not-empty | | | `wrapUpCode` | v3+ | The Menu Name of the wrap-up code list item that was used by the agent (optional) | in, not-in, contains, is-empty, is-not-empty | | | `wrapUpCodeId` | v3+ | The ID of the wrap-up code list item that was used by the agent (optional) | in, not-in, contains, is-empty, is-not-empty | | | `wrapUpCodeList` | v3+ | The wrap-up code list the code is part of (optional) | in, not-in, contains, is-empty, is-not-empty | | | `wrapUpCodeListId` | v3+ | The ID of the wrap-up code list the code is part of (optional) | in, not-in, contains, is-empty, is-not-empty | | | `wrapUpCodeText` | v3+ | The Report Text of the wrap-up code list item that was used by the agent (optional) | in, not-in, contains, is-empty, is-not-empty | | | `wrapUpShortCode` | v3+ | The Short Code of the wrap-up code list item that was used by the agent (optional) | in, not-in, contains, is-empty, is-not-empty | |
## 3.2. Agent Status Change Detailed Report Metrics Glossary This glossary provides comprehensive definitions for all metrics available in the `detailed-reports-agent-status-change` report type. Use this reference when selecting metrics for your reports and understanding the data returned.
Click to expand Metrics Glossary (14 metrics) **Version** indicates minimum CC Historical Analytics API version where metric became available. | Field | Version | Description | Allowed Filter Operators | Predefined Filter Values | |----------------------------------|---------|-------------|----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `agent` | v4+ | The name of the agent for whom the status change was recorded | in, not-in, contains, is-empty, is-not-empty | | | `agentGroup` | v4+ | The name of the specific group to which an agent belongs | in, not-in, contains, is-empty, is-not-empty | | | `agentGroupId` | v4+ | The unique identifier of the specific group to which an agent belongs | in, not-in, contains, is-empty, is-not-empty | | | `agentId` | v4+ | The unique identifier for the agent | in, not-in, contains, is-empty, is-not-empty | | | `interactionId` | v4+ | Contact Center's internal interaction ID. This identifier links the agent actions to active interactions (optional) | in, not-in, contains, is-empty, is-not-empty | | | `status` | v4+ | The agent status change or action that occurred. Possible values:• **loggedIn**: Agent logged into the system• **loggedOut**: Agent logged out of the system• **available**: Agent is available for interactions• **onBreak**: Agent is on break• **workingOffline**: Agent is working offline• **stopNew**: Agent stopped accepting new interactions• **resumeStopNew**: Agent resumed accepting new interactions• **wrapUp**: Agent is in wrap-up state• **offered**: Interaction was offered to the agent• **initiated**: Agent initiated an interaction• **accepted**: Agent accepted an offered interaction• **rejected**: Agent rejected an offered interaction• **timeout**: Agent did not respond to interaction offer within timeout• **handling**: Agent is actively handling an interaction• **putOnHold**: Agent placed interaction on hold• **holdResumed**: Agent resumed interaction from hold• **muted**: Agent muted their audio during the interaction• **unmuted**: Agent unmuted their audio during the interaction | in, not-in, contains, is-empty, is-not-empty | onBreak, available, workingOffline, offered, initiated, accepted, rejected, timeout, stopNew, resumeStopNew, wrapUp, loggedIn, loggedOut, handling, putOnHold, holdResumed, muted, unmuted | | `statusCode` | v4+ | The Menu Name of the status code item chosen by the agent (optional) | in, not-in, contains, is-empty, is-not-empty | | | `statusCodeId` | v4+ | The ID of the status code item (optional) | in, not-in, contains, is-empty, is-not-empty | | | `statusCodeList` | v4+ | The code list the code is part of (optional) | in, not-in, contains, is-empty, is-not-empty | | | `statusCodeListId` | v4+ | The ID of the status code list the code is part of (optional) | in, not-in, contains, is-empty, is-not-empty | | | `statusCodeShortCode` | v4+ | The Short Code of the status code item (optional) | in, not-in, contains, is-empty, is-not-empty | | | `statusCodeText` | v4+ | The Report Text of the status code item (optional) | in, not-in, contains, is-empty, is-not-empty | | | `time` | v4+ | The date and time stamp of the status change event | Not filterable | | | `transactionId` | v4+ | Also known as the "Token ID" or just "Transaction" in the old reports. This identifier links the agent actions to active interactions (optional) | in, not-in, contains, is-empty, is-not-empty | |
## 4. Creating a report > 📘 **This sample is applicable to ALL detailed report types** > > The values in passed in will be specific to the report-type but the concepts are applicable to all detailed reports. > > ### Parameters **Method:** POST #### Headers | Name | Required | Description | Example | | ------------- | -------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------ | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | ------- | -------- | --------------------------- | ------- | | version | ✓ | The current version is `v8` | v8 | #### Body | Name | Required | Description | Example | | ------------------------------------------ | -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | type | ✓ | The report type. Acceptable values are the types returned from the `report-types` API | agent-status-by-status-code | | title | ✓ | The report title, which allows only the characters listed below: letters from A to Z, a to z, 0 to 9, whitespaces or ! - \_ . \* ' ( ). If the report is later downloaded as a file, the title is used as the filename. | agent-status-by-status-code | | dateRange.start | ✓ | This parameter specifies that only events and records on or after the specified date are in the report. The entered values should follow the ISO 8061 standard (YYYY-MM-DDTHH:MM:SS.SSSZ) (For example, 2019-09-01T23:00:00.000Z) | | | dateRange.end | ✓ | This parameter specifies that only events and records on or before the specified date are included in the report. The entered values should follow the ISO 8061 standard. (YYYY-MM-DDTHH:MM:SS.SSSZ) (For example, 2019-09-01T23:00:00.000Z) | | | timezone | ☐ | The desired timezone (([IANA Time Zones](https://www.iana.org/time-zones). Examples America/New_York, Europe/Helsinki [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones))) that is applicable to current metrics only. Accepted timezone values are those that are configured for the tenant. The value can be the tenant’s default timezone or a value defined as an optional timezone. If no value is specified, the tenant’s default timezone is used | Europe/Helsinki | | intraDayTimeRange.start | ☐ | See [IntraDayTimeRange](/analytics/docs/cc-historical-analytics-summary-report#intradaytimerange). The start time for the intraDayTimeRange. The format is hh:mm:ss | 08:30:00 | | intraDayTimeRange.end | ☐ | See [IntraDayTimeRange](/analytics/docs/cc-historical-analytics-summary-report#intradaytimerange). The end time for the intraDayTimeRange. The end must be at least 5 minutes after the start. The format is hh:mm:ss | 17:00:00 | | metrics | ☐ | Can be omitted and all available metrics will be returned, or an array of `metrics` can be specified and only these metrics will be returned. See [metrics](/analytics/docs/cc-historical-analytics-detailed-report#metrics) | "metrics": ["accepted","acceptedInSla","acceptedInSlaPercentage","acceptedPercentage","totalAbandoned","totalAbandonedPercentage"] | | searchQuery | ☐ | See [searchQuery](/analytics/docs/cc-historical-analytics-detailed-report#searchquery) | true | | includeParticipants | ☐ | Only valid for detailed-reports-interaction-details report type. Default is false. see [includeParticipants](/analytics/docs/cc-historical-analytics-detailed-report#includeparticipants) | true | | reportSettings.showOngoingInteractions | ☐ | Allows interactions to be displayed that are not still ongoing and have partial data representing the current state of that interaction. | true | | reportSettings.showInteractionsStateInTime | ☐ | This parameter can be combined with *showOngoingInteractions* to show interactions as they were when providing a time range not up to the present, showing interactions in the ongoing state for when the time range ends. | true | #### intraDayTimeRange This parameter is used to specify a time range filter which applies within each day of the report. If this parameter is not specified, data will be returned for the complete time frame described in the mandatory dateRange object. > 🚧 **intraDayTimeRange minimum size** > > The *end* must be 5 minutes after the start for detailed reports and 15 minutes after the start for summary reports. > > * *start*: the start time for the intraDayTimeRange. The format is hh:mm:ss * *end*: the end time for the intraDayTimeRange. The format is hh:mm:ss If the requirement is to only see data between 8:30am and 5pm on each day the intraDayTimeRange would be passed as follows ```json "intraDayTimeRange": { "start": "08:30:00", "end": "17:00:00" } ``` #### metrics can be omitted and all available metrics will be returned, or an array of `metrics` can be specified and only these metrics will be returned. > 📘 **Metrics Example** > > If the requirement is to only have a subset of the the available metrics for the report type, we specify the required metrics > > * if no metrics are specified (omitted entirely or empty array): All metrics will be returned > * if metrics are specified: ONLY the specified metrics will be returned > > [Interaction Details Report Metrics Glossary](#31-interaction-details-report-metrics-glossary) and [Agent Status Change Detailed Report Metrics Glossary](#32-agent-status-change-detailed-report-metrics-glossary) provide detail on the definitions of the available metrics > > multiple metrics ```json "metrics": [ "agentNotes", "caseNumber", "channelId", "creationTime", "customerName", "destination", "direction", "finishedTime", "interactionId", "interactionType", "mediaType", "queueName", "transactionId", "ivrTreatmentDuration", "queueName" ] ``` #### searchQuery This parameter can be completely omitted Or an empty array can be passed to signify no filter. > 📘 **searchQuery logical operation.** > > The searchQuery object is an array which can include one or several objects, each acting as a filter containing the fields: field, operator, and value. If multiple filter objects are specified, the relation between them is logical AND > > > 📘 **searchQuery operators are type specific** > > * Numeric Fields ( durations like `busyDuration` and counts like `warmTransfersCompleted`: > * `=, <, >, !=, >=, <=, is-empty, is-not-empty` > * for durations (like `participantWrapUpDuration`, `ivrTreatmentDuration`) 1s => 1 second, 10m => 10 minutes, 3h => 3 hours, 7d => 7 days > * Id fields (like `interactionId`) and names (like `queueName`) `contains, is-empty, is-not-empty` > Sample Search Queries (click for details) no searchQueryagentNotes containsqueueName is one offilter queueName contains and ivrTreatmentDuration >= 10 seconds ```json "searchQuery": [ ] ``` ```json "searchQuery": [ { "field": "agentNotes", "operator": "contains", "value": "order" } ] ``` ```json "searchQuery": [ { "field": "queueName", "operator": "in", "value": ["US Sales", "EU Sales"] } ] ``` ```json "searchQuery":[ { "field": "queueName", "operator": "contains", "value": "Sales" }, { "field": "ivrTreatmentDuration", "operator": ">=", "value": "10s" } ] ``` ### Create Report Request In this example we are running the report from 3rd August to 2nd September, we are only interested in the periods between 8:30am and 5pm on each day. The data returned will only be where agentNotes contains "order" and queueName is "US Sales" or "EU Sales" and the ivrTreatementDuration was greater than or equal to 10 seconds. ```bash curl --location --request POST 'https://api.8x8.com/analytics/cc/v8/historical-metrics/detailed-reports' \ --header 'Authorization: Bearer {access_token}' \ --header 'Content-Type: application/json' \ --data-raw '{ "type": "detailed-reports-interaction-details", "title": "US-EU Sales-Orders-10-secondsIVR", "dateRange": { "start": "2022-08-03T00:00:00.000Z", "end": "2022-09-02T23:59:59.999Z" }, "timezone": "America/Chicago", "intraDayTimeRange": { "start": "08:30:00", "end": "17:00:00" }, "metrics":[ "agentNotes", "caseNumber", "channelId", "creationTime", "customerName", "destination", "direction", "finishedTime", "interactionId", "interactionType", "mediaType", "queueName", "transactionId", "ivrTreatmentDuration", "queueName" ], "searchQuery":[ { "field": "agentNotes", "operator": "contains", "value": "order" }, { "field": "ivrTreatmentDuration", "operator": ">=", "value": "10s" }, { "field": "queueName", "operator": "in", "value": ["US Sales", "EU Sales"] } ] }' ``` ### Create Report Response For an accepted request to create a report the response will be 200 OK #### Headers * **Link**: The Link header will provide details on how to access the data ```text [https://api.8x8.com/analytics/cc/v</historical-metrics/detailed-reports/2853641/data?size=100>; rel="data"` ``` > 📘 **Successful Creation always results in DONE** > > There is no need to poll for status with detailed reports, this is a variation from summary reports. > > #### Body * **id**: this is the identifier for the generated report * **status**: this is the status of the request for a newly created report. * DONE : the report has been generated * FAILED : the report has failed to generate ```json { "id": 2710192, "status": "DONE" } ``` ## 5. Accessing Report Data > 🚧 **Accessing the report Data** > > The data is available via JSON ONLY from `historical-metrics/detailed-reports` > > CSV/XLS responses are available from the `historical-metrics` endpoint use the [CC Historical Analytics Summary Report](/analytics/docs/cc-historical-analytics-summary-report) process for CSV/XLSX output of detailed reports > > ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version is `v8` | v8 | | report-id | ✓ | Report id to get data for | 2853641 | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | size | ☐ | The number of records to return in each page. Default is 100. Maximum is 1000 | 100 | | lastDocumentId | ☐/✓ | This MUST BE OMITTED in the request for the initial page. Then the value MUST BE PASSED in subsequent requests See [Headers & Pagination](/analytics/docs/cc-historical-analytics-detailed-report#headers--pagination) for more information | agent-status-by-status-code | ### Report Data Request First PageSubsequent Page ```bash curl --location --request GET 'api.8x8.com/analytics/cc/v8/historical-metrics/detailed-reports/2853641/data?size=100' \ --header 'Authorization: Bearer access_token' ``` ```bash curl --location --request GET 'api.8x8.com/analytics/cc/v8/historical-metrics/detailed-reports/2853641/data?size=100&lastDocumentId=eyJzZWFyY2hBZnRlciI6WzE2NDQ0NzA0MzQ5NzIsOTQ1MDExNzMwMjhdfQ%3D%3D' \ --header 'Authorization: Bearer access_token' ``` ### Report Data Response #### Headers & Pagination * **Link**: The Link header will provide a link to the next page in the data if there are additional pages by using the lastDocumentId to index into the result set `[https://api.8x8.com/analytics/cc/v</historical-metrics/detailed-reports/2710192/data?size=3&lastDocumentId=eyJzZWFyY2hBZnRlciI6WzE2NDQ0NzA0MzQ5NzIsOTQ1MDExNzMwMjhdfQ%3D%3D>; rel="next"` * **X-Page-Size**: size of the requested pages * **X-Total-Pages**: total number of pages for the report, 1 if only one page. * **X-Total-Elements**: total number of elements for the report * **Last-Document-Id**: the id of the last record in this page. Input to subsequent request to get the next page. **Pagination** When requesting the first page the `lastDocumentId` **MUST BE OMMITTED** When requesting subsequent pages use the returned value of the Last-Document-Id header in as the `lastDocumentId` of the following request. The Last-Document-Id will change on each request. When the last page is reached: * `Link` Header will not be present * `Last-Document-Id` Header will not be present #### Body The body will be an array as shown below. * The array could be empty if there are no records in the result * If not empty the array will contain one or more objects as described here * **total**: is `null` for detailed reports * **items**: array of the dimensions and metrics being returned. There will be one object for each. * *key*: the value will be the name of the dimension/metric * *label*: the value will be the human friendly name of the dimension/metric * *value*: the value will be the value of the dimension/metric. The value could be a string, could represent an array of values (ex. *["Queued", "Handled" ]*) or an object. The *finishedTime* metric has the following format: ```json { "key": "finishedTime", "label": "Finished Time", "value": { "value": "2023-07-03T16:02:59.857+03:00", "ongoing": false } } ``` The following duration metrics have the bellowed format : *queueWaitDuration, ivrTreatmentDuration, interactionDuration, customerJourneyDuration, participantOfferDuration, participantHandlingDuration, participantWrapUpDuration, participantProcessingDuration, participantBusyDuration, participantHoldDuration, participantLongestHoldDuration, participantMuteDuration, participantLongestMuteDuration, participantFocusDuration, participantLongestFocusDuration* ```json { "key": "interactionDuration", "label": "Interaction Duration", "value": { "value": 32281, "ongoing": false } } ``` where the `value` field is the value of the metric itself, could be represented as a time format in case of *finishedTime* metric and as milliseconds for the rest of the above duration metrics. The `ongoing` field shows if the value is on its final state (*=false*) or the value could still be changed since the interaction is still ongoing (*=true*). To get also the ongoing interactions the *reportSettings.showOngoingInteractions* request parameter should be set to *true*, otherwise the ongoing interactions will not be returned and the *ongoing* metric field will always be *false*. Sample example response: ```json [ { "total": null, "items": [ { "key": "time", "label": "Time", "value": "2023-07-03T16:02:27.576+03:00" }, { "key": "agentNotes", "label": "Agent Notes", "value": null }, { "key": "creationTime", "label": "Creation Time", "value": "2023-07-03T16:02:27.576+03:00" }, { "key": "customerName", "label": "Customer Name", "value": null }, { "key": "destination", "label": "Destination", "value": "16693335195" }, { "key": "direction", "label": "Direction", "value": "OutboundDir" }, { "key": "dispositionAction", "label": "Disposition Action", "value": null }, { "key": "externalTransactionData", "label": "External Transaction Data", "value": null }, { "key": "finishedTime", "label": "Finished Time", "value": { "value": "2023-07-03T16:02:59.857+03:00", "ongoing": false } }, { "key": "interactionDuration", "label": "Interaction Duration", "value": { "value": 32281, "ongoing": false } }, { "key": "interactionId", "label": "Interaction ID", "value": "int-1891bd8e8f8-DcOBzKtHEIa7sb0WsK7VtUCdB-phone-01-analyticsna12manu01" }, { "key": "interactionLabels", "label": "Labels", "value": [ "Queued", "Handled" ] }, { "key": "mediaType", "label": "Media Type", "value": "Phone" }, { "key": "participantBusyDuration", "label": "Busy Duration", "value": { "value": 32277, "ongoing": false } }, { "key": "participantHandlingDuration", "label": "Handling Duration", "value": { "value": 16347, "ongoing": false } } ] } ] ``` #### includeParticipants When *detailed-reports-interaction-details* report type is created with *includeParticipants* =`true` flag, the report response return also the `participants` field which contains the following participant metrics: * `blindTransferToAgent` * `blindTransferToQueue'` * `conferencesEstablished` * `consultationsEstablished` * `participantAssignNumber` * `participantBusyDuration` * `participantFocus` * `participantFocusDuration` * `participantHandlingDuration` * `participantHandlingEndTime` * `participantHold` * `participantHoldDuration` * `participantId` * `participantLongestFocusDuration` * `participantLongestHoldDuration` * `participantName` * `participantOfferAction` * `participantOfferActionTime` * `participantOfferDuration` * `participantOfferTime` * `participantProcessingDuration` * `participantType` * `participantWrapUpDuration` * `participantWrapUpEndTime` * `warmTransfersCompleted` * `wrapUpCode` * `wrapUpCodeId` * `wrapUpCodeList` * `wrapUpCodeListId` * `wrapUpCodeText` * `wrapUpShortCode` Sample response when includeParticipants is true ```json [ { "total": null, "items": [ { "key": "participants", "label": "Participants", "value": [ { "participantAssignNumber": 1, "participantType": "Agent", "participantId": "agsN41dY9PQtyhLSd9_xqQeg", "participantName": "Vlad Supervisor 2", "participantOfferTime": "2022-09-14T23:58:31.347-07:00", "participantOfferAction": "OfferTimeout", "participantOfferActionTime": "2022-09-14T23:59:01.350-07:00", "participantOfferDuration": { "value": 2438, "ongoing": false }, "participantHandlingEndTime": "2023-07-03T16:02:46.364+03:00", "participantHandlingDuration": { "value": 16347, "ongoing": false }, "participantWrapUpEndTime": "2023-07-03T16:02:59.856+03:00", "participantWrapUpDuration": { "value": 13492, "ongoing": false }, "participantProcessingDuration": { "value": 29839, "ongoing": false }, "participantBusyDuration": { "value": 32277, "ongoing": false }, "warmTransfersCompleted": 0, "blindTransferToAgent": 0, "blindTransferToQueue": 0, "consultationsEstablished": 0, "conferencesEstablished": 0, "participantHold": 0, "participantHoldDuration": 0, "participantLongestHoldDuration": 0, "wrapUpCode": [], "wrapUpCodeId": [], "wrapUpCodeList": [], "wrapUpCodeListId": [], "wrapUpCodeText": [], "wrapUpShortCode": [] }, { "participantAssignNumber": 2, "participantType": "Agent", "participantId": "agsN41dY9PQtyhLSd9_xqQeg", "participantName": "Vlad Supervisor 2", "participantOfferTime": "2022-09-14T23:59:46.074-07:00", "participantOfferAction": "Accepted", "participantOfferActionTime": "2022-09-14T23:59:50.001-07:00", "participantOfferDuration": { "value": 2438, "ongoing": false }, "participantHandlingEndTime": "2023-07-03T16:02:46.364+03:00", "participantHandlingDuration": { "value": 16347, "ongoing": false }, "participantWrapUpEndTime": "2023-07-03T16:02:59.856+03:00", "participantWrapUpDuration": { "value": 13492, "ongoing": false }, "participantProcessingDuration": { "value": 29839, "ongoing": false }, "participantBusyDuration": { "value": 32277, "ongoing": false }, "warmTransfersCompleted": 0, "blindTransferToAgent": 0, "blindTransferToQueue": 0, "consultationsEstablished": 0, "conferencesEstablished": 0, "participantHold": 0, "participantHoldDuration": 0, "participantLongestHoldDuration": 0, "wrapUpCode": [], "wrapUpCodeId": [], "wrapUpCodeList": [], "wrapUpCodeListId": [], "wrapUpCodeText": [], "wrapUpShortCode": [] } ] }, { "key": "time", "label": "Time", "value": "2022-09-14T23:58:31.234-07:00" }, { "key": "agentNotes", "label": "Agent Notes", "value": null }, { "key": "blindTransferToAgent", "label": "Blind Transfer To Agent", "value": "0" }, { "key": "blindTransferToQueue", "label": "Blind Transfer To Queue", "value": "0" }, { "key": "campaignId", "label": "Campaign ID", "value": null }, { "key": "campaignName", "label": "Campaign Name", "value": null }, { "key": "caseFollowUp", "label": "Case Follow Up", "value": null }, { "key": "caseNumber", "label": "Case Number", "value": "0" }, { "key": "channelId", "label": "Channel ID", "value": "roxana_chat_channel1" }, { "key": "conferencesEstablished", "label": "Conferences Established", "value": "0" }, { "key": "consultationsEstablished", "label": "Consultations Established", "value": "0" }, { "key": "creationTime", "label": "Creation Time", "value": "2022-09-14T23:58:31.234-07:00" }, { "key": "customerName", "label": "Customer Name", "value": null }, { "key": "destination", "label": "Destination", "value": null }, { "key": "direction", "label": "Direction", "value": "InboundDir" }, { "key": "dispositionAction", "label": "Disposition Action", "value": null }, { "key": "externalTransactionData", "label": "External Transaction Data", "value": null }, { "key": "finishedTime", "label": "Finished Time", "value": "2022-09-14T23:59:57.040-07:00" }, { "key": "interactionId", "label": "Interaction ID", "value": "int-1833ff122bc-9a3951e984fe40a6a010ed5802d3088f-chat-01-analyticsna12manu01" }, { "key": "interactionLabels", "label": "Labels", "value": [ "Queued", "OfferingTimeout", "Handled" ] }, { "key": "interactionType", "label": "Type", "value": null }, { "key": "ivrTreatmentDuration", "label": "IVR Treatment Duration", "value": null }, { "key": "mediaType", "label": "Media Type", "value": "Chat" }, { "key": "originalInteractionId", "label": "Original Interaction ID", "value": null }, { "key": "originalTransactionId", "label": "Original Transaction ID", "value": null }, { "key": "origination", "label": "Origination", "value": null }, { "key": "outboundPhoneCode", "label": "Outbound Phone Code", "value": null }, { "key": "outboundPhoneCodeId", "label": "Outbound Phone Code ID", "value": null }, { "key": "outboundPhoneCodeList", "label": "Outbound Phone Code List", "value": null }, { "key": "outboundPhoneCodeListId", "label": "Outbound Phone Code List ID", "value": null }, { "key": "outboundPhoneCodeText", "label": "Outbound Phone Code Text", "value": null }, { "key": "outboundPhoneShortCode", "label": "Outbound Phone Short Code", "value": null }, { "key": "participantAssignNumber", "label": "Assign #", "value": "2" }, { "key": "participantBusyDuration", "label": "Busy Duration", "value": "0:00:41" }, { "key": "participantHandlingDuration", "label": "Handling Duration", "value": "0:00:04" }, { "key": "participantHandlingEndTime", "label": "Handling End Time", "value": "2022-09-14T23:59:54.300-07:00" }, { "key": "participantHold", "label": "Hold", "value": "0" }, { "key": "participantHoldDuration", "label": "Hold Duration", "value": "0:00:00" }, { "key": "participantId", "label": "Participant ID", "value": [ "agsN41dY9PQtyhLSd9_xqQeg" ] }, { "key": "participantLongestHoldDuration", "label": "Longest Hold Duration", "value": "0:00:00" }, { "key": "participantName", "label": "Participant", "value": [ "Vlad Supervisor 2" ] }, { "key": "participantOfferAction", "label": "Offer Action", "value": [ "Accepted", "OfferTimeout" ] }, { "key": "participantOfferActionTime", "label": "Offer Action Time", "value": "2022-09-14T23:59:01.350-07:00" }, { "key": "participantOfferDuration", "label": "Offer Duration", "value": "0:00:34" }, { "key": "participantOfferTime", "label": "Offer Time", "value": "2022-09-14T23:58:31.347-07:00" }, { "key": "participantProcessingDuration", "label": "Processing Duration", "value": "0:00:07" }, { "key": "participantType", "label": "Participant Type", "value": [ "Agent" ] }, { "key": "participantWrapUpDuration", "label": "Wrap Up Duration", "value": "0:00:03" }, { "key": "participantWrapUpEndTime", "label": "Wrap Up End Time", "value": "2022-09-14T23:59:57.039-07:00" }, { "key": "queueId", "label": "Queue ID", "value": "412" }, { "key": "queueName", "label": "Queue Name", "value": "roxana_chat_queue" }, { "key": "queueTime", "label": "Queue Time", "value": "2022-09-14T23:58:31.346-07:00" }, { "key": "queueWaitDuration", "label": "Queue Wait Duration", "value": "0:01:19" }, { "key": "recordId", "label": "Record ID", "value": null }, { "key": "transactionId", "label": "Transaction ID", "value": "1816" }, { "key": "warmTransfersCompleted", "label": "Warm Transfers Completed", "value": "0" }, { "key": "wrapUpCode", "label": "Wrap Up Code", "value": [] }, { "key": "wrapUpCodeId", "label": "Wrap Up Code ID", "value": [] }, { "key": "wrapUpCodeList", "label": "Wrap Up Code List", "value": [] }, { "key": "wrapUpCodeListId", "label": "Wrap Up Code List ID", "value": [] }, { "key": "wrapUpCodeText", "label": "Wrap Up Code Text", "value": [] }, { "key": "wrapUpShortCode", "label": "Wrap Up Short Code", "value": [] } ] }, { "total": null, "items": [ { "key": "participants", "label": "Participants", "value": [] }, { "key": "time", "label": "Time", "value": "2022-09-14T10:15:50.320-07:00" }, { "key": "agentNotes", "label": "Agent Notes", "value": null }, { "key": "blindTransferToAgent", "label": "Blind Transfer To Agent", "value": null }, { "key": "blindTransferToQueue", "label": "Blind Transfer To Queue", "value": null }, { "key": "campaignId", "label": "Campaign ID", "value": null }, { "key": "campaignName", "label": "Campaign Name", "value": null }, { "key": "caseFollowUp", "label": "Case Follow Up", "value": null }, { "key": "caseNumber", "label": "Case Number", "value": null }, { "key": "channelId", "label": "Channel ID", "value": "12029635128" }, { "key": "conferencesEstablished", "label": "Conferences Established", "value": null }, { "key": "consultationsEstablished", "label": "Consultations Established", "value": null }, { "key": "creationTime", "label": "Creation Time", "value": "2022-09-14T10:15:50.320-07:00" }, { "key": "customerName", "label": "Customer Name", "value": "Relative Ferdinand" }, { "key": "destination", "label": "Destination", "value": null }, { "key": "direction", "label": "Direction", "value": "InboundDir" }, { "key": "dispositionAction", "label": "Disposition Action", "value": null }, { "key": "externalTransactionData", "label": "External Transaction Data", "value": null }, { "key": "finishedTime", "label": "Finished Time", "value": "2022-09-14T10:16:55.513-07:00" }, { "key": "interactionId", "label": "Interaction ID", "value": "int-1833cfff2af-L7NxZXoe0ttnABJQ2innePQuP-phone-01-analyticsna12manu01" }, { "key": "interactionLabels", "label": "Labels", "value": [ "Queued", "Dequeued" ] }, { "key": "interactionType", "label": "Type", "value": null }, { "key": "ivrTreatmentDuration", "label": "IVR Treatment Duration", "value": "0:00:05" }, { "key": "mediaType", "label": "Media Type", "value": "Phone" }, { "key": "originalInteractionId", "label": "Original Interaction ID", "value": null }, { "key": "originalTransactionId", "label": "Original Transaction ID", "value": null }, { "key": "origination", "label": "Origination", "value": "2068096167" }, { "key": "outboundPhoneCode", "label": "Outbound Phone Code", "value": null }, { "key": "outboundPhoneCodeId", "label": "Outbound Phone Code ID", "value": null }, { "key": "outboundPhoneCodeList", "label": "Outbound Phone Code List", "value": null }, { "key": "outboundPhoneCodeListId", "label": "Outbound Phone Code List ID", "value": null }, { "key": "outboundPhoneCodeText", "label": "Outbound Phone Code Text", "value": null }, { "key": "outboundPhoneShortCode", "label": "Outbound Phone Short Code", "value": null }, { "key": "participantAssignNumber", "label": "Assign #", "value": null }, { "key": "participantBusyDuration", "label": "Busy Duration", "value": null }, { "key": "participantHandlingDuration", "label": "Handling Duration", "value": null }, { "key": "participantHandlingEndTime", "label": "Handling End Time", "value": null }, { "key": "participantHold", "label": "Hold", "value": null }, { "key": "participantHoldDuration", "label": "Hold Duration", "value": null }, { "key": "participantId", "label": "Participant ID", "value": null }, { "key": "participantLongestHoldDuration", "label": "Longest Hold Duration", "value": null }, { "key": "participantName", "label": "Participant", "value": null }, { "key": "participantOfferAction", "label": "Offer Action", "value": null }, { "key": "participantOfferActionTime", "label": "Offer Action Time", "value": null }, { "key": "participantOfferDuration", "label": "Offer Duration", "value": null }, { "key": "participantOfferTime", "label": "Offer Time", "value": null }, { "key": "participantProcessingDuration", "label": "Processing Duration", "value": null }, { "key": "participantType", "label": "Participant Type", "value": null }, { "key": "participantWrapUpDuration", "label": "Wrap Up Duration", "value": null }, { "key": "participantWrapUpEndTime", "label": "Wrap Up End Time", "value": null }, { "key": "queueId", "label": "Queue ID", "value": "332" }, { "key": "queueName", "label": "Queue Name", "value": "CexInboundDemo" }, { "key": "queueTime", "label": "Queue Time", "value": "2022-09-14T10:15:55.443-07:00" }, { "key": "queueWaitDuration", "label": "Queue Wait Duration", "value": "0:01:00" }, { "key": "recordId", "label": "Record ID", "value": null }, { "key": "transactionId", "label": "Transaction ID", "value": "1807" }, { "key": "warmTransfersCompleted", "label": "Warm Transfers Completed", "value": null }, { "key": "wrapUpCode", "label": "Wrap Up Code", "value": null }, { "key": "wrapUpCodeId", "label": "Wrap Up Code ID", "value": null }, { "key": "wrapUpCodeList", "label": "Wrap Up Code List", "value": null }, { "key": "wrapUpCodeListId", "label": "Wrap Up Code List ID", "value": null }, { "key": "wrapUpCodeText", "label": "Wrap Up Code Text", "value": null }, { "key": "wrapUpShortCode", "label": "Wrap Up Short Code", "value": null } ] } ] ``` --- ## CC Historical Analytics Summary Report Customers looking to access data in JSON or CSV/XLSX from [CC Historical Analytics](/analytics/reference/cc-historical-report-create) can follow the this multi step process. > 📘 **You will need a working API key to begin** > > [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > The base URL is region specific, based on the location of your Contact Center tenant. * United States: `api.8x8.com/analytics/cc/{version}/historical-metrics/` * Europe: `api.8x8.com/eu/analytics/cc/{version}/historical-metrics/` * Asia-Pacific: `api.8x8.com/au/analytics/cc/{version}/historical-metrics/` * Canada: `api.8x8.com/ca/analytics/cc/{version}/historical-metrics/` * {version} to be replaced by current Version. As of August 2025 this is 8 resulting in /v8/ ## 1. Authenticate to retrieve access token [OAuth Authentication for 8x8 XCaaS APIs](/analytics/docs/oauth-authentication-for-8x8-xcaas-apis) is used to get a temporary `access_token` for use in with this API **Outputs For Next Step:** * access_token * expires_in The following steps will use the access_token as a Bearer Token form of authentication. This takes the form of the `Authorization` header being set to `Bearer access_token` (Space between Bearer and the access_token) > 📘 **JSON Examples shown, XML Also available** > > This guide shows all the examples in JSON. It is possible to retrieve responses in XML by specifying the following header > > `Accept: application/xml` > > ## 2 Multitenancy support If the API is used for a multitenant customer the requests should contain "X-Tenant-Info" header variable where needs to specify the desired tenantId. The "X-Tenant-Info" header is not mandatory in case of a single tenant customer. The following error messages could be returned when dealing with a multitenant customer: * if for a multitenant customer request the *"X-Tenant-Info"* header is not provided the HTTP 400 code along with *"Bad request: X-Tenant-Info header is missing."* message will be returned * if a wrong tenantId is provided the HTTP 400 code along with *"Bad request: Invalid value for X-Tenant-Info header."* message will be returned ## 3. Get Available Report Types CC Historical Analytics allows the consumer to get a listing of the available reports including information about their options and available data. Additional information about each of the reports and detailed definitions of metrics can be found in the [Metrics Glossary](#8-metrics-glossary) > 📘 **Script Paths Report** > > The `script-paths` report is available **on v8+**. See [Script Paths Report](#9-script-paths-report) for its request shape, restrictions, and CSV output format. > > ### Parameters **Method: GET** #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version is `v8` | v8 | | report-type | ☐ | Specific report type to get information on. Omit this parameter to get all report types. | agent-status-by-status-code | [API reference](/analytics/reference/cc-historical-analytics-report-types) ### Report Types Request The definition of a single report can also be retrieved by adding the `report-type` to the path all report typessingle report type ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v8/historical-metrics/report-types' \ --header 'Accept: application/json;charset=UTF-8' \ --header 'Authorization: Bearer {access_token}' ``` ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v8/historical-metrics/report-types/agent-status-by-status-code' \ --header 'Accept: application/json;charset=UTF-8' \ --header 'Authorization: Bearer {access_token}' ``` ### Report Types Response The response shows each `report-type` that's available. > 📘 **Detailed Reports** > > This returns some "detailed" report types as well as the summary reports. Detailed reports have a different format described in the [CC Historical Analytics Detailed Report Guide](/analytics/docs/cc-historical-analytics-detailed-report) > > **Outputs For Next Step:** For the summary reports the response has a number of elements to guide the usage: * `type` each report type has a unique definition * `groupBy` each report has one or more groupBy options. This specifies the grouping for the output * `name` this is the name to specify when creating a report with a specific grouping * `filters` these are the available filters for this report type for this particular grouping. The filters available vary depending on the type AND the grouping. * `metrics` these are the available metrics for the report type. When creating a report. See the [Metrics Glossary](#8-metrics-glossary) for additional detail on the definition of the available metrics * When running reports: * if no metrics are specified: All metrics will be returned * if metrics are specified: ONLY the specified metrics will be returned The body will be an array as shown below. * The array will contain one or more objects as described here * **type**: this is the report type and name of the report * **groupBy**: array of options for grouping the report by various dimensions * *name*: name of the grouping * *filters*: array of the possible filtering options for this grouping for this report * **value**: array of the metrics available for this report ```json [ { "type": "report type name", "groupBy": [ { "name": "name of grouping 1", "filters": [ "filterable dimension 1", "filterable dimension 2" ] }, { "name": "name of grouping 2", "filters": [ "filterable dimension 1", "filterable dimension 2", "filterable dimension 3", ] } ], "metrics": [ "report metric 1", "report metric 2" ] } ] ``` **Sample Response for single report type** The result **will be different** for each report type. ```json { "type": "agent-interactions-summary", "groupBy": [ { "name": "agent", "filters": [ "agent" ] }, { "name": "agent-and-media", "filters": [ "agent", "media" ] }, { "name": "agent-and-media-and-channel", "filters": [ "agent", "media" ] }, { "name": "agent-and-media-and-channel-and-queue", "filters": [ "agent", "media", "queue" ] }, { "name": "agent-and-media-and-queue", "filters": [ "agent", "media", "queue" ] }, { "name": "group", "filters": [ "group" ] }, { "name": "group-and-agent", "filters": [ "agent", "group" ] }, { "name": "group-and-agent-and-media", "filters": [ "agent", "group", "media" ] }, { "name": "group-and-agent-and-media-and-channel", "filters": [ "agent", "group", "media" ] }, { "name": "group-and-agent-and-media-and-channel-and-queue", "filters": [ "agent", "group", "media", "queue" ] }, { "name": "group-and-agent-and-media-and-queue", "filters": [ "agent", "group", "media", "queue" ] }, { "name": "group-and-media", "filters": [ "group", "media" ] }, { "name": "group-and-media-and-channel", "filters": [ "group", "media" ]}, { "name": "group-and-media-and-channel-and-queue", "filters": [ "group", "media", "queue" ] }, { "name": "group-and-media-and-queue", "filters": [ "group", "media", "queue" ] } ], "metrics": [ "abandoned", "abandonedPercentage", "accepted", "acceptedPercentage", "alerting", "avgBusyTime", "avgFocusTime", "avgHandlingTime", "avgHoldTime", "avgSpeedToAnswer", "avgWrapUpTime", "blindTransferToAgent", "blindTransferToQueue", "blindTransfersInitiated", "blindTransfersReceived", "busyTime", "focusTime", "handlingTime", "hold", "holdTime", "longestHoldTime", "longestOfferingTime", "offeringTime", "presented", "rejectTimeout", "rejected", "rejectedPercentage", "transfersInitiated", "transfersInitiatedPercentage", "transfersReceived", "warmTransfersCompleted", "warmTransfersReceived", "wrapUpTime" ] } ``` ## 4. Creating A Summary Report > 📘 **This sample is applicable to ALL summary report types** > > The values in passed in will be specific to the report-type but the concepts are applicable to all summary report types. > > > 📘 **Script Paths uses a different request shape** > > The `script-paths` type has its own request shape and additional constraints; see [Script Paths Report](#9-script-paths-report). > > ### Parameters **Method:** POST #### Headers | Name | Required | Description | Example | | ------------- | -------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------ | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | | Content-Type | ✓ | Set Content-Type to application/json | application/json | #### Path | Name | Required | Description | Example | | ----------- | -------- | ---------------------------------------------------------------------------------------- | --------------------------- | | version | ✓ | The current version is `v8` | v8 | | report-type | ☐ | Specific report type to get information on. Omit this parameter to get all report types. | agent-status-by-status-code | #### Body | Name | Required | Description | Example | | ----------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | type | ✓ | The report type. Acceptable value is any one of the types returned from the `report-types` API | agent-status-by-status-code | | title | ✓ | The report title, which allows only the characters listed below: letters from A to Z, a to z, 0 to 9, whitespaces or ! - \_ . \* ' ( ). If the report is later downloaded as a file, the title is used as the filename. | Agent Status By Code Aug Sep | | dateRange.start | ✓ | This parameter specifies that only events and records on or after the specified date are in the report. The entered values should follow the ISO 8061 standard (YYYY-MM-DDTHH:MM:SS.SSSZ) (For example, 2019-09-01T23:00:00.000Z) | | | dateRange.end | ✓ | This parameter specifies that only events and records on or before the specified date are included in the report. The entered values should follow the ISO 8061 standard. (YYYY-MM-DDTHH:MM:SS.SSSZ) (For example, 2019-09-01T23:00:00.000Z) | | | granularity | ✓ | This parameter specifies how to aggregate the report data by time intervals. You must use one of the following values: 15m, 30m, hour, day, week, month, year, or none. See [granularity](/analytics/docs/cc-historical-analytics-summary-report#granularity) for more information. | 15m | | groupBy.name | ✓ | This parameter controls how your data should be grouped by dimensions. It must be one of the grouping options returned by report-type for the specified report type. | media-and-channel-and-queue | | groupBy,filters[] | ☐ | Filters are an array of names and values that describes the dimension to filter by and the values of those filter(s). See [filters](/analytics/docs/cc-historical-analytics-summary-report#filters) for more detail. | [filters](/analytics/docs/cc-historical-analytics-summary-report#filters) | | timezone | ☐ | The desired timezone ([IANA Time Zones](https://www.iana.org/time-zones). Examples America/New_York, Europe/Helsinki [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)) that is applicable to current metrics only. Accepted timezone values are those that are configured for the tenant. The value can be the tenant’s default timezone or a value defined as an optional timezone. If no value is specified, the tenant’s default timezone is used | Europe/Helsinki | | intraDayTimeRange.start | ☐ | See [IntraDayTimeRange](/analytics/docs/cc-historical-analytics-summary-report#intradaytimerange). The start time for the intraDayTimeRange. The format is hh:mm:ss | 08:30:00 | | intraDayTimeRange.end | ☐ | See [IntraDayTimeRange](/analytics/docs/cc-historical-analytics-summary-report#intradaytimerange). The end time for the intraDayTimeRange. The end must be at least 15 minutes after the start. The format is hh:mm:ss | 17:00:00 | | metrics | ☐ | Can be omitted and all available metrics will be returned, or an array of `metrics` can be specified and only these metrics will be returned. | "metrics": [ "accepted","acceptedInSla","acceptedInSlaPercentage","acceptedPercentage","totalAbandoned","totalAbandonedPercentage" ] | | includeSubTotal | ☐ | (Default `false`) This parameter adds subtotals rows in the report. It accepts only Boolean values written as `true` or `false` or as strings listed as `"true"` or `"false"`. | true | | includeGrandTotal | ☐ | (Default `false`)This parameter puts the grand total row in the report. It accepts only Boolean values written as `true` or `false` or as strings listed as `"true"` or `"false"` | true | #### granularity This parameter specifies how to aggregate the report data by time intervals. You must use one of the following values: 15m, 30m, hour, day, week, month, year, or none. * If the assigned parameter value is none, then the report data is not aggregated by time. * For date range intervals less than or equal to a week the accepted > - granularities are 15m, 30m, hour, day, or none. * For date range intervals less than or equal to a month and but longer than a week the accepted granularities are none, hour, day, or week * For date range intervals longer than a month the accepted granularities are none, month, or year #### intraDayTimeRange This parameter is used to specify a time range filter which applies within each day of the report. If this parameter is not specified, data will be returned for the complete time frame described in the mandatory dateRange object. > 🚧 **intraDayTimeRange minimum size** > > The *end* must be 15 minutes after the start for summary reports and 5 minutes after the start for detailed reports. > > * *start*: the start time for the intraDayTimeRange. The format is hh:mm:ss * *end*: the end time for the intraDayTimeRange. The format is hh:mm:ss If the requirement is to only see data between 8:30am and 5pm on each day the intraDayTimeRange would be passed as follows ```json "intraDayTimeRange": { "start": "08:30:00", "end": "17:00:00" } ``` In version 6 intraDayTimeRange was enhanced to to cover cross-day time range filtering in reports allowing to generate a single report for overnight shifts, and it is available for all aggregated and detailed report types. You can now generate reports for overnight shifts of specific time ranges that cross two days. With the following example can be generated a report which cover activities for a week but for only time intervals from 20:00 to 06:00 time range. ```json "dateRange": { "start": "2022-08-05T00:00:00.00Z", "end": "2022-08-11T00:00:00.00Z" }, "intraDayTimeRange": { "start": "20:00:00", "end": "06:00:00" } ``` #### filters Parameter can be completely omitted Or an empty array can be passed for no filtering. For example report type `queue-interactions-summary` and groupBy `media-and-channel-and-queue` we can chose to not filter at all OR filter by `media` and or `queue` Each report type has it's own filtering capabilies which can be found in the [Report Types Response](/analytics/docs/cc-historical-analytics-summary-report#report-types-response) each `groupBy` has it's own applicable filters. no filtersingle queuefilter on multiple queuesfilter on multiple queues and multiple media ```json "filters": [ ] ``` ```json "filters": [ { "name": "queue", "values": ["103"] } ] ``` ```json "filters": [ { "name": "queue", "values": ["103", "330"] } ] ``` ```json "filters": [ { "name": "queue", "values": ["103", "330"] }, { "name": "media", "values": ["Phone", "Chat"] } ] ``` *Notes:* * When filtering by `media`, if the user also wants to filter by phone or email direction, instead of using `{"name": "media", "values": ["Phone"]}`, the phone direction can be specified as follows: `{"name": "media", "values": ["OutboundPhone"]}` or `{"name": "media", "values": ["InboundPhone"]}`, and instead of using `{"name": "media", "values": ["Email"]}`, the email direction can be specified as follows: `{"name": "media", "values": ["OutboundEmail"]}` or `{"name": "media", "values": ["InboundEmail"]}` * When creating an `agent-interactions-by-wrap-up-code` report type, and the customer wants to filter by `wrap-up-code`, the filter values should be formatted as follows: `{"name": "wrap-up-code", "values": ["-"]}`. The `` and `` can be found in the CCA UI detailed reports by adding the 'Wrap Up Code List ID' and 'Wrap Up Code ID' fields to the report. For example: `{"name": "wrap-up-code", "values": ["170-1201"]}` #### metrics If the requirement is to only have a subset of the the available metrics for the report type, we specify the required metrics * if no metrics are specified (omitted entirely or empty array) ==> All metrics will be returned * if metrics are specified ==> ONLY the specified metrics will be returned The [Metrics Glossary](#8-metrics-glossary) provides detail on the definition of the available metrics ```json "metrics": [ "accepted", "acceptedInSla", "acceptedInSlaPercentage", "acceptedPercentage", "totalAbandoned", "totalAbandonedPercentage" ] ``` [API reference](/analytics/reference/cc-historical-report-create) ### Create Report Request In this example we are running the report from 3rd August to 2nd September, we are only interested in the periods between 8:30am and 5pm on each day and we are grouping the data by media, channel and queue at a weekly granularity. The data returned will only be for queue id is 103 and 330 and only if the media is Phone or Chat and the metrics returned will be only the ones specified, with sub and grand totals. ```bash curl --location --request POST 'https://api.8x8.com/analytics/cc/v8/historical-metrics' \ --header 'Authorization: Bearer {access_token}' \ --header 'Content-Type: application/json' \ --data-raw '{ "type": "queue-interactions-summary", "title": "Weeky Queue Report for OPS", "dateRange": { "start": "2022-08-03T00:00:00.000Z", "end": "2022-09-02T00:00:00.000Z" }, "granularity": "week", "groupBy":{ "name":"media-and-channel-and-queue", "filters": [ { "name": "queue", "values": ["103", "330"] }, { "name": "media", "values": ["Phone", "Chat"] } ] }, "timezone": "America/New_York", "intraDayTimeRange": { "start": "08:30:00", "end": "17:00:00" }, "metrics": [ "accepted", "acceptedInSla", "acceptedInSlaPercentage", "acceptedPercentage", "totalAbandoned", "totalAbandonedPercentage" ], "includeGrandTotal": true, "includeSubTotal": true }' ``` ### Create Report Response For an accepted request to create a report the response will be 200 OK #### Headers * Link => The Link header will provide details on how to check the status of the create request ```text [https://api.8x8.com/analytics/cc/v</historical-metrics/2710192/status; rel="status"> ``` #### Body * **id**: this is the identifier for the generated report * **status**: this is the status of the request to create the report * IN_PROGRESS : the report is being generated, usually the initial status * DONE : the report has been generated * FAILED : the report has failed to generate ```json { "id": 2710192, "status": "IN_PROGRESS" } ``` ## 5. Get Report Status ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version is `v8` | v8 | | report-id | ✓ | report id returned in the create report request. | 2710192 | [API reference](/analytics/reference/cc-historical-report-status-by-id) ### Report Status Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v8/historical-metrics/2710192/status' \ --header 'Authorization: Bearer access_token' ``` ### Report Status response This will be the same format as the response from creating the report. Recheck the status periodically until the status is `"DONE"`. > 🚧 **Don't check status in a tight loop (please)** > > Leave some time between status checks, repeatedly requesting updates without taking a pause is more likely to slow the response than speed it up. > > #### Headers The Link header WILL ONLY be present if the report staus is `"DONE"` * Link => The Link header will provide details on how access the data and download for the report ```text [https://api.8x8.com/analytics/cc/v</historical-metrics/2710663/data?page=0&size=100>; rel="data", [https://api.8x8.com/analytics/cc/v</historical-metrics/2710663/download>; rel="download" ``` #### Body * **id**: this is the identifier for the generated report * **status**: this is the status of the request to create the report * IN_PROGRESS : the report is being generated, usually the initial status * DONE : the report has been generated * FAILED : the report has failed to generate ```json { "id": 2710192, "status": "IN_PROGRESS" } ``` ## 6a. Get Report Data (JSON) > 📘 **Accessing the report Data** > > The data is available via JSON or via CSV/XLSX. To access the data as JSON the data endpoint is used, for CSV/XLSX the download endpoint is used. > > > 🚧 **Data (JSON) results are capped at 10,000 records.** > > CSV/XLS will return all larger result sets. > > Detailed Reports have an alternative approach since larger result sets are expected. > > > 🚧 **Not supported for Script Paths reports** > > The `/data` endpoint is not available for Script Paths reports — the API returns **400 Bad Request**. Use [6b. Get Report Download](#6b-get-report-download-csvxlsx) instead. > > ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version is `v8` | v8 | | report-id | ✓ | report id returned in the create report request. | 2710192 | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | page | ☐ | (starts from 0) enables navigation to the expected page; if no value is specified then the first page is retrieved. Required on subsequent pages | 0 | | size | ☐ | gives the amounts of elements on one page. If no value is specified then default values are used (0 for page, 100 for size). Maximum page size is 1000 elements | 200 | [API reference](/analytics/reference/cc-historical-report-data-by-id) ### Report Data (JSON) Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v8/historical-metrics/2710192/data?page=0&size=100' \ --header 'Authorization: Bearer {access_token}' ``` ### Report Data (JSON) Response #### Headers * Link => The Link header will provide a link to the next page in the data if there are additional pages. **Will not be present if there are no more pages.** ```text [https://api.8x8.com/analytics/cc/v</historical-metrics/2710663/data?page=1&size=100>; rel="next" ``` * **X-Page**: current page number, 0(zero) is the first page * **X-Page-Size**: size of the requested pages * **X-Total-Pages**: total number of pages for the report, 1 if only one page. * **X-Total-Elements**: total number of elements for the report #### Body The body will be an array as shown below. * The array could be empty if there are no records in the result * If not empty the array will contain one or more objects as described here * **total**: if this represents a subtotal or grandtotal (only present if "includeGrandTotal": true, "includeSubTotal": true were requested) * **items**: array of the dimensions and metrics being returned. There will be one object for each. * *key*: the value will be the name of the dimension/metric * *label*: the value will be the human friendly name of the dimension/metric * *value*: the value will be the value of the dimension/metric. This is ALWAYS a string. ```json [ { "total": null, "items": [ { "key": "name of key", "label": "Human friendly label of key", "value": "string representation of value", }, { "key": "name of key", "label": "Human friendly label of key", "value": "string representation of value", } ] }, { "total": { "type": "subtotal", "startIndex": 0, "endIndex": 1 }, "items": [ { "key": "name of key", "label": "Human friendly label of key", "value": "string representation of value", }, { "key": "name of key", "label": "Human friendly label of key", "value": "string representation of value", } ] } , { "total": { "type": "grandtotal", "startIndex": null, "endIndex": null }, "items": [ { "key": "name of key", "label": "Human friendly label of key", "value": "string representation of value", }, { "key": "name of key", "label": "Human friendly label of key", "value": "string representation of value", } ] } ] [ { "total": null, "items": { "name1": "value1", "name2": 3 "name3": "2022-09-02T00:00:00.000Z", } }, { "total": { "type": "subtotal", "startIndex": 0, "endIndex": 1 }, "items": [ { "key": "name of key", "label": "Human friendly label of key", "value": "string representation of value", }, { "key": "name of key", "label": "Human friendly label of key", "value": "string representation of value", } ] } , { "total": { "type": "grandtotal", "startIndex": null, "endIndex": null }, "items": [ { "key": "name of key", "label": "Human friendly label of key", "value": "string representation of value", }, { "key": "name of key", "label": "Human friendly label of key", "value": "string representation of value", } ] } ] ``` > 🚧 **Dimension values for `subtotal` and `grandtotal` items** > > Where a subtotal or grandtotal is summarizing multiple instances of a single dimension the value for that item will be `null` since there is no single correct value > > ```json { "total": { "type": "subtotal", "startIndex": 23, "endIndex": 23 }, "items": [ { "key": "startTime", "label": "Start Time", "value": "2022-08-29T00:00-04:00" }, { "key": "endTime", "label": "End Time", "value": "2022-09-05T00:00-04:00" }, { "key": "media", "label": "Media", "value": "Phone" }, { "key": "channel", "label": "Channel", "value": null }, { "key": "queue", "label": "Queue", "value": null }, { "key": "queueId", "label": "Queue Id", "value": null }, { "key": "accepted", "label": "Accepted", "value": "0" } ] } ``` ## 6b. Get Report Download (CSV/XLSX) > 📘 **Accessing the report Data** > > The data is available via JSON or via CSV/XLSX. To access the data as JSON the data endpoint is used, for CSV/XLSX the download endpoint is used. > > > 📘 **There is no pagination the whole file will be returned.** > > > 📘 **Script Paths reports are CSV only** > > For Script Paths reports the download is always CSV — XLSX is not supported. See [Script Paths Report](#9-script-paths-report). > > ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | | ------------- | -------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------ | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | | Accept | ✓ | Specify the download type - CSV `text/csv`- XLSX `text/xlsx` | text\xlsx | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version is `v8` | v8 | | report-id | ✓ | report id returned in the create report request. | 2710192 | [API reference](/analytics/reference/cc-historical-report-download-by-id) ### Report Download (CSV/XLSX) Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v8/historical-metrics/2710192/download' \ --header 'Accept: text/xlsx' \ --header 'Authorization: Bearer {access_token}' ``` ### Report Download (CSV/XLSX) Response #### Headers * Content-Disposition => will contain information about the file generated, the filename will reflect the title input in the report creation with the xlsx or csv type extension added. Example: `attachment; filename="Weeky Queue Report for OPS.xlsx"` #### Body The file content is returned in the body. ## 7. Access Report Links ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version is `v8` | v8 | | report-id | ✓ | report id returned in the create report request. | 2710192 | [API reference](/analytics/reference/cc-historical-report-links-by-id) ### Report Links Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v8/historical-metrics/2710192/links' \ --header 'Authorization: Bearer {access_token}' ``` ### Report Links Response #### Body The body will be an array as shown below. * `status` is always shown * `data` and `download` are shown if the report status is DONE ```json [ { "relation": "status", "link": "https://api.8x8.com/analytics/cc/v8/historical-metrics/2684392/status" }, { "relation": "data", "link": "https://api.8x8.com/analytics/cc/v8/historical-metrics/2684392/data?page=0&size=100" }, { "relation": "download", "link": "https://api.8x8.com/analytics/cc/v8/historical-metrics/2684392/download" } ] ``` ## 8. Metrics Glossary This glossary provides comprehensive definitions for all metrics available across the aggregated report types. Use this reference when selecting metrics for your reports and understanding the data returned. ### 8.1. Agent Status Metrics Agent status metrics track how agents spend their time across different operational states. These metrics help analyze agent productivity, availability patterns, and time allocation. #### 8.1.1. Agent Status by Status Code
Click to expand Agent Status by Status Code Metrics (4 metrics) Report type: `agent-status-by-status-code` This report breaks down agent time by specific status codes, showing how long agents spend in each configured status reason. **Version** indicates minimum CC Historical Analytics API version where metric became available. | Metric | Version | Description | |--------|---------|-------------| | `loggedInTime` | v7+ | Total time the agent maintained active system connection and was available for work across all queues and activities within the current aggregation interval | | `statusCodeCount` | v1+ | Number of times the agent entered or changed to a specific status code within the current aggregation interval. Tracks frequency of status changes for each configured status reason | | `statusCodeTime` | v1+ | Total time the agent spent in a specific status code within the current aggregation interval. Represents the cumulative duration for each configured status reason | | `timePercentage` | v7+ | Percentage of time the agent spent in a specific status code relative to total logged-in time within the current aggregation interval. Calculated as (statusCodeTime / loggedInTime) × 100 |
#### 8.1.2. Agent Status Logged In
Click to expand Agent Status Logged In Metrics (1 metric) Report type: `agent-status-logged-in` Tracks agent availability and productivity by showing the login sessions. **Version** indicates minimum CC Historical Analytics API version where metric became available. | Metric | Version | Description | |--------|---------|-------------| | `loggedInTime` | v1+ | Total duration of the agent's login session. Represents how long the agent maintained active system connection from login to logout |
#### 8.1.3. Agent Status Time on Status
Click to expand Agent Status Time on Status Metrics (15 metrics) Report type: `agent-status-time-on-status` This report breaks down agent time across major operational states, providing insight into how agents allocate their time between handling interactions, being available, taking breaks, and working offline. **Version** indicates minimum CC Historical Analytics API version where metric became available. | Metric | Version | Description | |--------|---------|-------------| | `availableTime` | v1+ | Total time the agent spent in Available state, ready to receive incoming interactions within the current aggregation interval | | `availableTimePercentage` | v1+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work within the current aggregation interval | | `busyTime` | v1+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities within the current aggregation interval | | `busyTimePercentage` | v1+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working within the current aggregation interval | | `handlingTime` | v1+ | Total time the agent spent in Handling state, actively processing interactions within the current aggregation interval | | `handlingTimePercentage` | v1+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions within the current aggregation interval | | `loggedInTime` | v1+ | Total time the agent maintained active system connection and was available for work across all queues and activities within the current aggregation interval | | `offeringTime` | v7+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities within the current aggregation interval | | `offeringTimePercent` | v7+ | Percentage of offering time relative to total logged-in time. Shows what proportion of login duration agent spent with interactions being offered within the current aggregation interval. Calculated as (offeringTime / loggedInTime) × 100 | | `onBreakTime` | v1+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions within the current aggregation interval | | `onBreakTimePercentage` | v1+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break within the current aggregation interval | | `workingOfflineTime` | v1+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions within the current aggregation interval | | `workingOfflineTimePercentage` | v1+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status within the current aggregation interval | | `wrapUpTime` | v1+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer within the current aggregation interval | | `wrapUpTimePercentage` | v1+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state within the current aggregation interval |
### 8.2. Queue Interactions Metrics Queue interactions metrics track how interactions flow through queues, including acceptance, abandonment, wait times, and agent handling performance. #### 8.2.1. Queue Interactions Summary
Click to expand Queue Interactions Summary Metrics (29 metrics) Report type: `queue-interactions-summary` This report provides comprehensive queue performance metrics, tracking interaction flow, agent handling, wait times, and abandonment patterns. **Version** indicates minimum CC Historical Analytics API version where metric became available. | Metric | Version | Description | |--------|---------|-------------| | `accepted` | v1+ | Total interactions answered by agents. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent in the current aggregation interval | | `acceptedInSla` | v1+ | Total number of interactions answered by all agents within the SLA Threshold Time. Measures interactions where the agent answered before the configured SLA time limit was exceeded in the current aggregation interval | | `acceptedInSlaPercentage` | v1+ | Percentage of total number of interactions answered by all agents within the SLA Threshold Time, relative to total accepted interactions in the current aggregation interval | | `acceptedPercentage` | v2+ | Percentage of total interactions answered by agents relative to total entries. Shows what proportion of all interactions entering the queue were successfully connected to and handled by an agent in the current aggregation interval | | `avgAbandonTime` | v2+ | Average time spent by all interactions in the queue waiting to be served that ended up as an abandonment (includes short abandoned) in the current aggregation interval | | `avgBusyTime` | v1+ | Average time agents spent in the Offering, Handling, and Wrap Up states per interaction in the current aggregation interval | | `avgHandlingTime` | v1+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold in the current aggregation interval | | `avgProcessingTime` | v1+ | Average combined time in Handling and Wrap Up states per accepted interaction. This represents the total time from when an agent accepts an interaction through final completion, including wrap-up work in the current aggregation interval | | `avgWaitBeforeAcceptedTime` | v1+ | Average time an interaction spent in the queue, from the time it entered the queue until it was accepted by an agent in the current aggregation interval | | `avgWaitTime` | v1+ | Average waiting time for interactions. Time interactions spend in queue from entry until acceptance, abandonment, or diversion in the current aggregation interval | | `avgWrapUpTime` | v1+ | Average post-processing time per interaction entered. Time spent by agents completing administrative tasks after finishing handling an interaction in the current aggregation interval | | `busyTime` | v1+ | Total time agents spent in the Offering, Handling, and Wrap Up states. Measured from when an interaction is presented to an agent until it is wrapped up in the current aggregation interval | | `diverted` | v1+ | Interactions leaving queue without termination via transfer, forwarding, or IVR routing. Represents interactions that were moved out of the queue through various routing mechanisms in the current aggregation interval | | `entered` | v1+ | Inbound interactions entering queue; outbound interactions directed through queue. Counts all interactions that came into this queue waiting to be processed in the current aggregation interval | | `handlingTime` | v1+ | Total time agents spent in the Handling state. Measured from when an interaction is accepted by an agent until it is terminated in the current aggregation interval | | `longestAbandonTime` | v2+ | Longest time an interaction spent waiting in a queue to be served and ended up as an abandonment in the current aggregation interval | | `longestWaitBeforeAcceptTime` | v7+ | Longest time an interaction spent in the queue from entry until it was accepted by an agent in the current aggregation interval | | `longestWaitTime` | v1+ | Longest wait in queue for interactions. Duration of the longest waiting interaction in the queue in the current aggregation interval | | `newInQueue` | v4+ | Interactions entering queue in the current aggregation interval only. Excludes interactions from previous intervals, showing only freshly entered interactions | | `parked` | v8+ | Number of interactions currently in parked state assigned to this queue at the end of the current aggregation interval. Snapshot-style (instantaneous) metric — only counts interactions still parked at interval end. Use with `accepted` to compute Parked %: `(parked / accepted) × 100` | | `processingTime` | v1+ | Total time agents spent in the Handling and Wrap Up states. Measured from when an interaction is accepted by an agent until it is wrapped up in the current aggregation interval | | `slaPercentage` | v1+ | Percentage of interactions answered before configured SLA time threshold relative to total entries, excluding short abandonments in the current aggregation interval | | `totalAbandonTime` | v2+ | Total cumulative time all abandoned interactions spent waiting in queue before abandonment in the current aggregation interval | | `totalAbandoned` | v1+ | All interactions finishing in abandonment including short abandonments. Provides complete picture of both quick and extended abandonments combined in the current aggregation interval | | `totalAbandonedPercentage` | v2+ | Percentage of abandoned interactions relative to total entries. Shows the complete abandonment rate including all types of abandonments in the current aggregation interval | | `voicemailsLeft` | v7+ | Number of interactions where customers left a voicemail in the current aggregation interval | | `waitingInQueue` | v1+ | Number of interactions waiting in queue to be answered at the end of the current aggregation interval | | `waitingInQueueTime` | v2+ | Total cumulative time all interactions spent waiting in queue in the current aggregation interval | | `wrapUpTime` | v1+ | Total time agents spent in the Wrap Up state completing post-interaction administrative tasks after disconnecting from customer in the current aggregation interval |
#### 8.2.2. Queue Interactions Accepted Offline
Click to expand Queue Interactions Accepted Offline Metrics (13 metrics) Report type: `queue-interactions-accepted-offline` This report tracks offline interactions (email, voicemail) accepted by agents, broken down by time buckets based on how long the interaction waited in queue before being answered. **Version** indicates minimum CC Historical Analytics API version where metric became available. | Metric | Version | Description | |--------|---------|-------------| | `accepted` | v1+ | Total interactions answered by agents. Represents every offline interaction that was successfully connected to and handled by an agent in the current aggregation interval | | `accepted.lowerThan30m` | v1+ | Number of interactions answered by agents in under 30 minutes from the moment the interaction entered the queue until it was answered in the current aggregation interval | | `accepted.30m-1h` | v1+ | Number of interactions answered by agents between 30 minutes and 1 hour from the moment the interaction entered the queue in the current aggregation interval | | `accepted.1h-1h30m` | v1+ | Number of interactions answered by agents between 1 and 1.5 hours from the moment the interaction entered the queue in the current aggregation interval | | `accepted.1h30m-2h` | v1+ | Number of interactions answered by agents between 1.5 and 2 hours from the moment the interaction entered the queue in the current aggregation interval | | `accepted.2h-3h` | v1+ | Number of interactions answered by agents between 2 and 3 hours from the moment the interaction entered the queue in the current aggregation interval | | `accepted.greaterThan3h` | v1+ | Number of interactions answered by agents in over 3 hours from the moment the interaction entered the queue in the current aggregation interval | | `acceptedPercentage.lowerThan30m` | v1+ | Percentage of accepted interactions in under 30 minutes over the total accepted interactions in the current aggregation interval. Calculated as (Accepted under 30 min / Accepted) × 100 | | `acceptedPercentage.30m-1h` | v1+ | Percentage of accepted interactions between 30 minutes and 1 hour over the total accepted interactions in the current aggregation interval. Calculated as (Accepted 30min-1h / Accepted) × 100 | | `acceptedPercentage.1h-1h30m` | v1+ | Percentage of accepted interactions between 1 and 1.5 hours over the total accepted interactions in the current aggregation interval. Calculated as (Accepted 1h-1h30m / Accepted) × 100 | | `acceptedPercentage.1h30m-2h` | v1+ | Percentage of accepted interactions between 1.5 and 2 hours over the total accepted interactions in the current aggregation interval. Calculated as (Accepted 1h30m-2h / Accepted) × 100 | | `acceptedPercentage.2h-3h` | v1+ | Percentage of accepted interactions between 2 and 3 hours over the total accepted interactions in the current aggregation interval. Calculated as (Accepted 2h-3h / Accepted) × 100 | | `acceptedPercentage.greaterThan3h` | v1+ | Percentage of accepted interactions in over 3 hours over the total accepted interactions in the current aggregation interval. Calculated as (Accepted over 3h / Accepted) × 100 |
#### 8.2.3. Queue Interactions Accepted Online
Click to expand Queue Interactions Accepted Online Metrics (21 metrics) Report type: `queue-interactions-accepted-online` This report tracks online interactions (phone, chat) accepted by agents, broken down by time buckets based on how long the interaction waited in queue before being answered. **Version** indicates minimum CC Historical Analytics API version where metric became available. | Metric | Version | Description | |--------|---------|-------------| | `accepted` | v1+ | Total interactions answered by agents. Represents every online interaction that was successfully connected to and handled by an agent in the current aggregation interval | | `accepted.lowerThan5s` | v1+ | Number of interactions answered by agents in under 5 seconds from the moment the interaction entered the queue until it was answered in the current aggregation interval | | `accepted.5s-10s` | v1+ | Number of interactions answered by agents between 5 and 10 seconds from the moment the interaction entered the queue until it was answered in the current aggregation interval | | `accepted.10s-20s` | v1+ | Number of interactions answered by agents between 10 and 20 seconds from the moment the interaction entered the queue until it was answered in the current aggregation interval | | `accepted.20s-30s` | v1+ | Number of interactions answered by agents between 20 and 30 seconds from the moment the interaction entered the queue until it was answered in the current aggregation interval | | `accepted.30s-45s` | v1+ | Number of interactions answered by agents between 30 and 45 seconds from the moment the interaction entered the queue until it was answered in the current aggregation interval | | `accepted.45s-1m` | v1+ | Number of interactions answered by agents between 45 seconds and 1 minute from the moment the interaction entered the queue until it was answered in the current aggregation interval | | `accepted.1m-2m` | v1+ | Number of interactions answered by agents between 1 and 2 minutes from the moment the interaction entered the queue until it was answered in the current aggregation interval | | `accepted.2m-5m` | v1+ | Number of interactions answered by agents between 2 and 5 minutes from the moment the interaction entered the queue until it was answered in the current aggregation interval | | `accepted.5m-10m` | v1+ | Number of interactions answered by agents between 5 and 10 minutes from the moment the interaction entered the queue until it was answered in the current aggregation interval | | `accepted.greaterThan10m` | v1+ | Number of interactions answered by agents in more than 10 minutes from the moment the interaction entered the queue until it was answered in the current aggregation interval | | `acceptedPercentage.lowerThan5s` | v1+ | Percentage of accepted interactions in under 5 seconds over the total accepted interactions in the current aggregation interval. Calculated as (Accepted under 5s / Accepted) × 100 | | `acceptedPercentage.5s-10s` | v1+ | Percentage of accepted interactions between 5 and 10 seconds over the total accepted interactions in the current aggregation interval. Calculated as (Accepted 5s-10s / Accepted) × 100 | | `acceptedPercentage.10s-20s` | v1+ | Percentage of accepted interactions between 10 and 20 seconds over the total accepted interactions in the current aggregation interval. Calculated as (Accepted 10s-20s / Accepted) × 100 | | `acceptedPercentage.20s-30s` | v1+ | Percentage of accepted interactions between 20 and 30 seconds over the total accepted interactions in the current aggregation interval. Calculated as (Accepted 20s-30s / Accepted) × 100 | | `acceptedPercentage.30s-45s` | v1+ | Percentage of accepted interactions between 30 and 45 seconds over the total accepted interactions in the current aggregation interval. Calculated as (Accepted 30s-45s / Accepted) × 100 | | `acceptedPercentage.45s-1m` | v1+ | Percentage of accepted interactions between 45 seconds and 1 minute over the total accepted interactions in the current aggregation interval. Calculated as (Accepted 45s-1m / Accepted) × 100 | | `acceptedPercentage.1m-2m` | v1+ | Percentage of accepted interactions between 1 and 2 minutes over the total accepted interactions in the current aggregation interval. Calculated as (Accepted 1m-2m / Accepted) × 100 | | `acceptedPercentage.2m-5m` | v1+ | Percentage of accepted interactions between 2 and 5 minutes over the total accepted interactions in the current aggregation interval. Calculated as (Accepted 2m-5m / Accepted) × 100 | | `acceptedPercentage.5m-10m` | v1+ | Percentage of accepted interactions between 5 and 10 minutes over the total accepted interactions in the current aggregation interval. Calculated as (Accepted 5m-10m / Accepted) × 100 | | `acceptedPercentage.greaterThan10m` | v1+ | Percentage of accepted interactions in more than 10 minutes over the total accepted interactions in the current aggregation interval. Calculated as (Accepted over 10m / Accepted) × 100 |
#### 8.2.4. Queue Interactions Abandoned
Click to expand Queue Interactions Abandoned Metrics (40 metrics) Report type: `queue-interactions-abandoned` This report tracks interactions that were abandoned in queue, broken down by time buckets based on how long the interaction waited before the customer terminated the interaction. Includes summary statistics on abandonment patterns. **Version** indicates minimum CC Historical Analytics API version where metric became available. | Metric | Version | Description | |--------|---------|-------------| | `abandon.lowerThan5s` | v1+ | Number of abandoned interactions where customer terminated in under 5 seconds from entering queue in the current aggregation interval | | `abandon.5s-10s` | v1+ | Number of abandoned interactions where customer terminated between 5 and 10 seconds from entering queue in the current aggregation interval | | `abandon.10s-20s` | v1+ | Number of abandoned interactions where customer terminated between 10 and 20 seconds from entering queue in the current aggregation interval | | `abandon.20s-30s` | v1+ | Number of abandoned interactions where customer terminated between 20 and 30 seconds from entering queue in the current aggregation interval | | `abandon.30s-45s` | v1+ | Number of abandoned interactions where customer terminated between 30 and 45 seconds from entering queue in the current aggregation interval | | `abandon.45s-1m` | v1+ | Number of abandoned interactions where customer terminated between 45 seconds and 1 minute from entering queue in the current aggregation interval | | `abandon.1m-2m` | v1+ | Number of abandoned interactions where customer terminated between 1 and 2 minutes from entering queue in the current aggregation interval | | `abandon.2m-5m` | v1+ | Number of abandoned interactions where customer terminated between 2 and 5 minutes from entering queue in the current aggregation interval | | `abandon.5m-10m` | v1+ | Number of abandoned interactions where customer terminated between 5 and 10 minutes from entering queue in the current aggregation interval | | `abandon.greaterThan10m` | v1+ | Number of abandoned interactions where customer terminated in more than 10 minutes from entering queue in the current aggregation interval | | `abandonPercentage.lowerThan5s` | v1+ | Percentage of Total Abandoned interactions in under 5 seconds over the total Entered interactions in the current aggregation interval | | `abandonPercentage.5s-10s` | v1+ | Percentage of Total Abandoned interactions between 5 and 10 seconds over the total Entered interactions in the current aggregation interval | | `abandonPercentage.10s-20s` | v1+ | Percentage of Total Abandoned interactions between 10 and 20 seconds over the total Entered interactions in the current aggregation interval | | `abandonPercentage.20s-30s` | v1+ | Percentage of Total Abandoned interactions between 20 and 30 seconds over the total Entered interactions in the current aggregation interval | | `abandonPercentage.30s-45s` | v1+ | Percentage of Total Abandoned interactions between 30 and 45 seconds over the total Entered interactions in the current aggregation interval | | `abandonPercentage.45s-1m` | v1+ | Percentage of Total Abandoned interactions between 45 seconds and 1 minute over the total Entered interactions in the current aggregation interval | | `abandonPercentage.1m-2m` | v1+ | Percentage of Total Abandoned interactions between 1 and 2 minutes over the total Entered interactions in the current aggregation interval | | `abandonPercentage.2m-5m` | v1+ | Percentage of Total Abandoned interactions between 2 and 5 minutes over the total Entered interactions in the current aggregation interval | | `abandonPercentage.5m-10m` | v1+ | Percentage of Total Abandoned interactions between 5 and 10 minutes over the total Entered interactions in the current aggregation interval | | `abandonPercentage.greaterThan10m` | v1+ | Percentage of Total Abandoned interactions in more than 10 minutes over the total Entered interactions in the current aggregation interval | | `abandoned` | v1+ | Number of interactions that terminated in the queue and ended with customer disconnecting, excluding short abandonments in the current aggregation interval | | `abandonedPercentage` | v1+ | Percentage of interactions that terminated in the queue without being served over total entries, excluding short abandonments in the current aggregation interval | | `accepted` | v1+ | Total interactions answered by agents. Represents every interaction that was successfully connected to and handled by an agent in the current aggregation interval | | `acceptedPercentage` | v2+ | Percentage of interactions answered by agents over total entries in the current aggregation interval | | `avgAbandonTime` | v2+ | Average time abandoned interactions spent waiting in queue in the current aggregation interval | | `diverted` | v1+ | Number of interactions entering and leaving the queue without ending. Includes interactions transferred to other queues or voicemail in the current aggregation interval | | `divertedPercentage` | v1+ | Percentage of diverted interactions over total entries in the current aggregation interval | | `entered` | v1+ | Inbound interactions entering queue; outbound interactions directed through queue. Counts all interactions that came into this queue waiting to be processed in the current aggregation interval | | `longestAbandonTime` | v1+ | Longest wait time for abandoned interaction. Duration of the interaction that waited longest before customer disconnected in the current aggregation interval | | `newInQueue` | v4+ | Interactions entering queue in the current aggregation interval only. Excludes interactions from previous intervals, showing only freshly entered interactions | | `offering` | v1+ | Number of interactions being offered to available agents awaiting acceptance or rejection at the end of the current aggregation interval | | `offeringPercentage` | v1+ | Percentage of interactions in offering state over total entries in the current aggregation interval | | `shortAbandoned` | v1+ | Number of short abandonments. Interactions ending with customer exit in queue before 5 seconds in the current aggregation interval | | `shortAbandonedPercentage` | v1+ | Percentage of short abandonments. Percentage of interactions ending with customer exit in queue before 5 seconds relative to total entries in the current aggregation interval | | `totalAbandonTime` | v2+ | Total cumulative time all abandoned interactions spent waiting in queue before abandonment in the current aggregation interval | | `totalAbandoned` | v1+ | All interactions finishing in abandonment including short abandonments. Provides complete picture of both quick and extended abandonments combined in the current aggregation interval | | `totalAbandonedPercentage` | v1+ | Percentage of all abandoned interactions (including short abandonments) relative to total entries in the current aggregation interval | | `voicemailsLeft` | v7+ | Number of interactions where customers left a voicemail in the current aggregation interval | | `waitingInQueue` | v1+ | Number of interactions waiting in queue to be answered at the end of the current aggregation interval | | `waitingInQueueTime` | v2+ | Total cumulative time all interactions spent waiting in queue in the current aggregation interval |
### 8.3. Agent Interactions Metrics Agent interactions metrics track agent activity across different interaction types, including handling times, transfers, and wrap-up activities. These metrics help analyze agent performance and interaction outcomes. #### 8.3.1. Agent Interactions by Wrap Up Code
Click to expand Agent Interactions by Wrap Up Code Metrics (2 metrics) Report type: `agent-interactions-by-wrap-up-code` This report tracks agent activity by wrap-up codes (transaction codes), showing how many times each code was used and the total processing time associated with interactions marked with that code. **Version** indicates minimum CC Historical Analytics API version where metric became available. | Metric | Version | Description | |--------|---------|-------------| | `transactionCodeCount` | v1+ | Total number of times the transaction code has been used in the current aggregation interval | | `transactionCodeTime` | v1+ | Total time agents spent processing interactions (Handling and Wrap Up) that finished with this transaction code in the current aggregation interval |
#### 8.3.2. Agent Interactions Call Summary
Click to expand Agent Interactions Call Summary Metrics (29 metrics) Report type: `agent-interactions-call-summary` This report provides comprehensive statistics on agent call activity including direct inbound/outbound calls, holds, transfers (blind and warm), conferences, consultations, and internal agent-to-agent calls. **Version** indicates minimum CC Historical Analytics API version where metric became available. | Metric | Version | Description | |--------|---------|-------------| | `avgDirectInboundTime` | v1+ | Average time agents spent on direct inbound calls, excluding agent-to-agent calls in the current aggregation interval | | `avgDirectOutboundTime` | v1+ | Average time agents spent on outbound calls, excluding outbound queue calls and agent-to-agent calls in the current aggregation interval | | `avgHoldTime` | v2+ | Average duration per hold action. Calculated as total Hold Time divided by Number of Holds in the current aggregation interval | | `blindTransferToAgent` | v1+ | Total blind transfers initiated and received by agents outside of a queue in the current aggregation interval | | `blindTransferToQueue` | v1+ | Total blind transfers to queues initiated by agents in the current aggregation interval | | `blindTransfersInitiated` | v1+ | Number of blind transfers performed by agents in the current aggregation interval. Transfer where agent does not speak to recipient first | | `blindTransfersReceived` | v1+ | Number of blind transfers received by agents in the current aggregation interval | | `conferenceTime` | v1+ | Total cumulative duration agents spent in multi-party conference calls in the current aggregation interval | | `conferences` | v1+ | Total number of conferences in the current aggregation interval | | `conferencesEstablished` | v1+ | Total number of conferences (join lines) agents have initiated in the current aggregation interval | | `conferencesEstablishedTime` | v1+ | Total time agents spent in conference calls that they initiated in the current aggregation interval | | `conferencesReceived` | v1+ | Total number of conferences (join lines) agents have received in the current aggregation interval | | `conferencesReceivedTime` | v1+ | Total time agents spent in conference calls that they received in the current aggregation interval | | `consultationsEstablished` | v1+ | Number of times agents successfully established an outbound call while another call is on hold in the current aggregation interval | | `directInbound` | v1+ | Total number of direct inbound calls to agents excluding agent-to-agent calls in the current aggregation interval | | `directInboundTime` | v1+ | Total cumulative duration agents spent on direct inbound calls, excluding agent-to-agent calls in the current aggregation interval | | `directOutbound` | v1+ | Number of calls made by agents excluding outbound queue calls and agent-to-agent calls in the current aggregation interval | | `directOutboundTime` | v1+ | Total cumulative duration agents spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls in the current aggregation interval | | `hold` | v1+ | Total number of call holds agents have performed in the current aggregation interval | | `holdTime` | v1+ | Total time agents spent placing customers or agents on hold on any line in the current aggregation interval | | `internalCalls` | v1+ | Total number of agent-to-agent calls initiated or received in the current aggregation interval | | `internalCallsInitiated` | v1+ | Number of agent-to-agent calls initiated in the current aggregation interval | | `internalCallsReceived` | v1+ | Number of agent-to-agent calls received in the current aggregation interval | | `internalCallsTime` | v1+ | Total cumulative duration agents spent on agent-to-agent calls, both initiated and received in the current aggregation interval | | `longestHoldTime` | v1+ | Maximum single continuous hold duration when agent placed customer on hold in the current aggregation interval | | `transfersInitiated` | v1+ | Warm and blind transfers initiated by agents. All outgoing transfers in the current aggregation interval | | `transfersReceived` | v1+ | Warm and blind transfers routed to agents for handling. All incoming transfers in the current aggregation interval | | `warmTransfersCompleted` | v1+ | Total number of warm transfers initiated by agents in the current aggregation interval. Transfer where agent spoke to recipient first | | `warmTransfersReceived` | v1+ | Total number of warm transfers received by agents in the current aggregation interval |
#### 8.3.3. Agent Interactions Handling and Wrap Up
Click to expand Agent Interactions Handling and Wrap Up Metrics (10 metrics) Report type: `agent-interactions-handling-and-wrap-up` This report tracks agent time spent across different interaction processing states: Offering (waiting for acceptance), Handling (actively working), Wrap Up (post-call tasks), and combined Busy time. **Version** indicates minimum CC Historical Analytics API version where metric became available. | Metric | Version | Description | |--------|---------|-------------| | `avgBusyTime` | v1+ | Average combined time in Offering, Handling, and Wrap Up states per interaction. Includes the full duration from when an interaction is offered, through handling, until all work is complete in the current aggregation interval | | `avgHandlingTime` | v1+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold in the current aggregation interval | | `avgOfferingTime` | v1+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it in the current aggregation interval | | `avgProcessingTime` | v1+ | Average combined time in Handling and Wrap Up states per accepted interaction. This represents the total time from when an agent accepts an interaction through final completion, including wrap-up work in the current aggregation interval | | `avgWrapUpTime` | v1+ | Average post-processing time per interaction entered. Time spent by agents completing administrative tasks after finishing handling an interaction in the current aggregation interval | | `busyTime` | v1+ | Total combined time in Offering, Handling, and Wrap Up states. Includes the full duration from when an interaction is offered, through handling, until all work is complete in the current aggregation interval | | `handlingTime` | v1+ | Total time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold in the current aggregation interval | | `offeringTime` | v1+ | Total duration from interaction presentation to acceptance or rejection in the current aggregation interval | | `processingTime` | v1+ | Total combined time in Handling and Wrap Up states per accepted interaction. This represents the total time from when an agent accepts an interaction through final completion, including wrap-up work in the current aggregation interval | | `wrapUpTime` | v1+ | Total time spent by agents completing administrative tasks after finishing handling an interaction in the current aggregation interval |
#### 8.3.4. Agent Interactions Summary
Click to expand Agent Interactions Summary Metrics (36 metrics) Report type: `agent-interactions-summary` This report provides a comprehensive overview of agent performance including interaction acceptance/rejection rates, handling times, transfers, and hold statistics. Combines metrics from call activity, handling times, and wrap-up activities. **Version** indicates minimum CC Historical Analytics API version where metric became available. | Metric | Version | Description | |--------|---------|-------------| | `abandoned` | v1+ | Total number of interactions abandoned by a customer while being presented to the agent in the current aggregation interval | | `abandonedPercentage` | v1+ | Percentage of interactions abandoned while being presented to the agent over the total interactions presented in the current aggregation interval | | `accepted` | v1+ | Total interactions answered by agents. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent in the current aggregation interval | | `acceptedPercentage` | v1+ | Percentage of interactions answered by agents over the total interactions presented in the current aggregation interval | | `alerting` | v1+ | Number of interactions currently being presented to agents via a queue or direct assignment in the current aggregation interval | | `assignedInteractions` | v8+ | Number of interactions assigned to the agent at the end of the current aggregation interval. Includes interactions in handling, wrap-up, and parked states; excludes offering. Snapshot-style (instantaneous) metric. Used as the denominator for client-computed Parked %: `(parked / assignedInteractions) × 100` | | `avgBusyTime` | v1+ | Average combined time in Offering, Handling, and Wrap Up states per interaction. Includes the full duration from when an interaction is offered, through handling, until all work is complete in the current aggregation interval | | `avgFocusTime` | v8+ | Average time agents spent with digital interactions (chat, email) in focus in the current aggregation interval. We consider that a digital interaction is in focus when an agent actively views and works on the interaction window, as opposed to switching to other tasks such as consulting knowledge bases or documentation | | `avgHandlingTime` | v1+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold in the current aggregation interval | | `avgHoldTime` | v2+ | Average duration per hold action. Calculated as total Hold Time divided by Number of Holds in the current aggregation interval | | `avgSpeedToAnswer` | v1+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it in the current aggregation interval | | `avgWrapUpTime` | v1+ | Average post-processing time per interaction entered. Time spent by agents completing administrative tasks after finishing handling an interaction in the current aggregation interval | | `blindTransferToAgent` | v1+ | Total blind transfers initiated and received by agents outside of a queue in the current aggregation interval | | `blindTransferToQueue` | v1+ | Total blind transfers to queues initiated by agents in the current aggregation interval | | `blindTransfersInitiated` | v1+ | Number of blind transfers performed by agents in the current aggregation interval. Transfer where agent does not speak to recipient first | | `blindTransfersReceived` | v1+ | Number of blind transfers received by agents in the current aggregation interval | | `busyTime` | v1+ | Total combined time in Offering, Handling, and Wrap Up states. Includes the full duration from when an interaction is offered, through handling, until all work is complete in the current aggregation interval | | `focusTime` | v8+ | Total cumulative time agents spent with digital interactions (chat, email) in focus in the current aggregation interval. We consider that a digital interaction is in focus when an agent actively views and works on the interaction window, as opposed to switching to other tasks such as consulting knowledge bases or documentation | | `handlingTime` | v1+ | Total time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold in the current aggregation interval | | `hold` | v1+ | Total number of call holds agents have performed in the current aggregation interval | | `holdTime` | v1+ | Total time agents spent placing customers or agents on hold on any line in the current aggregation interval | | `longestHoldTime` | v1+ | Maximum single continuous hold duration when agent placed customer on hold in the current aggregation interval | | `longestOfferingTime` | v1+ | Longest time to accept an interaction from the time it is offered to the time it is accepted or rejected by an agent in the current aggregation interval | | `offeringTime` | v1+ | Total duration from interaction presentation to acceptance or rejection in the current aggregation interval | | `parked` | v8+ | Number of interactions currently in parked state assigned to the agent / agent group at the end of the current aggregation interval. Snapshot-style (instantaneous) metric — only counts interactions still parked at interval end. Use with `assignedInteractions` to compute Parked %: `(parked / assignedInteractions) × 100` | | `presented` | v1+ | Total interactions presented to agents for acceptance or rejection. Includes interactions continuing from prior intervals in the current aggregation interval | | `processingTime` | v7+ | Total combined time in Handling and Wrap Up states per accepted interaction. This represents the total time from when an agent accepts an interaction through final completion, including wrap-up work in the current aggregation interval | | `rejectTimeout` | v1+ | Count of interactions automatically rejected when agent did not respond within configured timeout period in the current aggregation interval | | `rejected` | v1+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer in the current aggregation interval | | `rejectedPercentage` | v1+ | Percentage of interactions rejected by agents over the total interactions presented in the current aggregation interval | | `transfersInitiated` | v1+ | Warm and blind transfers initiated by agents. All outgoing transfers in the current aggregation interval | | `transfersInitiatedPercentage` | v2+ | Percentage of interactions transferred by agents, calculated relative to total interactions accepted in the current aggregation interval | | `transfersReceived` | v1+ | Warm and blind transfers routed to agents for handling. All incoming transfers in the current aggregation interval | | `warmTransfersCompleted` | v1+ | Total number of warm transfers initiated by agents in the current aggregation interval. Transfer where agent spoke to recipient first | | `warmTransfersReceived` | v1+ | Total number of warm transfers received by agents in the current aggregation interval | | `wrapUpTime` | v1+ | Total time spent by agents completing administrative tasks after finishing handling an interaction in the current aggregation interval |
### 8.4. Digital Channels Metrics Digital channels metrics track performance for non-voice interactions such as email and chat. These metrics measure interaction volumes and durations across different processing stages. #### 8.4.1. Digital Channels Summary
Click to expand Digital Channels Summary Metrics (10 metrics) Report type: `digital-channels-summary` This report displays aggregate metrics for digital channels such as email and chat, tracking interaction volumes, and durations across different processing stages. **Version** indicates minimum CC Historical Analytics API version where metric became available. | Metric | Version | Description | |--------|---------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `avgSpeedToAnswer` | v1+ | Average duration from interaction presentation to acceptance for digital channel interactions in the current aggregation interval | | `countOfInteractions` | v1+ | Total count of digital channel interactions processed in the current aggregation interval | | `handlingDuration` | v1+ | Total time agents spend handling digital channel interactions. Measured from when an agent accepts an interaction until they finish processing it in the current aggregation interval | | `numberOfRepliedEmails` | v1+ | Total count of email interactions that received agent replies in the current aggregation interval | | `offeringDuration` | v1+ | Total duration digital channel interactions spent in Offering state waiting for agent acceptance or rejection in the current aggregation interval | | `parkDuration` | v8+ | Total cumulative time digital channel interactions spent in parked state in the current aggregation interval. Parked duration is NOT included in `handlingDuration` | | `queueWaitDuration` | v1+ | Total cumulative time digital channel interactions spent waiting in queue in the current aggregation interval | | `scriptTreatmentDuration` | v1+ | Total time digital channel interactions spent in script treatment, excluding queue time in the current aggregation interval | | `totalInteractionsDuration` | v1+ | Total combined duration for digital channel interactions. Includes script treatment, queue wait, and handling time in the current aggregation interval | | `wrapUpDuration` | v1+ | Total time agents spend completing post-interaction administrative tasks for digital channel interactions. Measured from when an agent disconnects from customer until wrap-up is finalized in the current aggregation interval |
## 9. Script Paths Report The Script Paths report (**v8+**) provides a hierarchical view of the paths taken by interactions through an **IVR script** — showing how calls flow through scripts and IVR nodes, with a count of interactions per node. Results are delivered as a downloadable CSV. > 📘 **Differences from other summary reports** > > * Export format is CSV only — XLSX is not supported. > * Results are retrieved through [6b. Get Report Download](#6b-get-report-download-csvxlsx). The `/data` endpoint is not available for Script Paths reports — the API returns **400 Bad Request**. > * Only [`/{id}/download`](/analytics/reference/cc-historical-report-download-by-id) is returned in the links response when the report is `DONE` (no `/data` link). > * The request body MUST NOT include `groupBy`, `granularity`, `metrics`, `includeSubTotal`, `includeGrandTotal`, or `includeParticipants`. > > ### 9.1. Create Script Paths Report **Method:** POST #### Headers | Name | Required | Description | Example | | ------------- | -------- | -------------------------------------------------------------------------------------------------------- | ------------------------------------ | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | | Content-Type | ✓ | Set Content-Type to application/json | application/json | #### Path | Name | Required | Description | Example | | ------- | -------- | --------------------------- | ------- | | version | ✓ | The current version is `v8` | v8 | #### Body | Name | Required | Description | Example | | ----------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------- | | type | ✓ | Must be `script-paths`. | script-paths | | title | ✓ | The report title. Used as the downloaded file name. | Script Paths Weekly | | dateRange.start | ✓ | Only interactions created on or after the specified date are included. The entered values should follow the ISO 8601 standard (YYYY-MM-DDTHH:MM:SS.SSSZ), for example 2026-04-01T00:00:00.000Z. | 2026-04-01T00:00:00.000Z | | dateRange.end | ✓ | Only interactions created on or before the specified date are included. The entered values should follow the ISO 8601 standard (YYYY-MM-DDTHH:MM:SS.SSSZ), for example 2026-04-08T00:00:00.000Z. | 2026-04-08T00:00:00.000Z | | timezone | ✓ | The desired timezone ([IANA Time Zones](https://www.iana.org/time-zones). Examples: `America/New_York`, `Europe/Helsinki`, see [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)). Accepted values are those configured for the tenant. If no value is specified, the tenant's default timezone is used. | Europe/Helsinki | | intraDayTimeRange.start | ☐ | See [IntraDayTimeRange](#intradaytimerange). The start time for the intraDayTimeRange. The format is hh:mm:ss | 08:30:00 | | intraDayTimeRange.end | ☐ | See [IntraDayTimeRange](#intradaytimerange). The end time for the intraDayTimeRange. The format is hh:mm:ss | 17:00:00 | | searchQuery[] | ☐ | Optional filters. Each entry has `field`, `operator`, and `value`. See [searchQuery](#searchquery-script-paths) below. | See below | ##### searchQuery (Script Paths) * **`field`** — one of `script`, `queue`, `agent`, `channel` (lowercase; case-sensitive). * **`operator`** — `in`. * **`value`** — array of string IDs; see the example below. ```json "searchQuery": [ { "field": "agent", "operator": "in", "value": ["agfDAzC5NtSRuHol5GA4RT6A", "ag10000", "ag7sfo_qfaTXCvwb7MsSUStw"] }, { "field": "channel", "operator": "in", "value": ["1zkbCLd_R3ij4bC0T_hY7w", "ZOLa_Q-DSlSpdfq--H9vPQ", "ITPIhMTWQf-mpiQ_lahDQw"] }, { "field": "queue", "operator": "in", "value": ["169", "872"] }, { "field": "script", "operator": "in", "value": ["4163", "5471"] } ] ``` [API reference](/analytics/reference/cc-historical-report-create) #### Request ```bash curl --location --request POST 'https://api.8x8.com/analytics/cc/v8/historical-metrics' \ --header 'Authorization: Bearer {access_token}' \ --header 'Content-Type: application/json' \ --data-raw '{ "type": "script-paths", "title": "Script Paths Weekly", "dateRange": { "start": "2026-04-01T00:00:00.000Z", "end": "2026-04-08T00:00:00.000Z" }, "timezone": "Europe/Helsinki", "intraDayTimeRange": { "start": "08:30:00", "end": "17:00:00" }, "searchQuery": [ { "field": "agent", "operator": "in", "value": ["agfDAzC5NtSRuHol5GA4RT6A", "ag10000", "ag7sfo_qfaTXCvwb7MsSUStw"] }, { "field": "channel", "operator": "in", "value": ["1zkbCLd_R3ij4bC0T_hY7w", "ZOLa_Q-DSlSpdfq--H9vPQ", "ITPIhMTWQf-mpiQ_lahDQw"] }, { "field": "queue", "operator": "in", "value": ["169", "872"] }, { "field": "script", "operator": "in", "value": ["4163", "5471"] } ] }' ``` #### Response The response shape is identical to other report types (returns an `id` and `status`). Poll `/status` until `DONE`, then download the CSV. ### 9.2. Retrieving the Result 1. Poll the status — see [5. Get Report Status](#5-get-report-status). 2. When status is `DONE`, download the CSV — see [6b. Get Report Download](#6b-get-report-download-csvxlsx). The response is always CSV for this report type. 3. The links endpoint ([7. Access Report Links](#7-access-report-links)) returns only the download link when `DONE`. ### 9.3. CSV Column Reference
Click to expand Script Paths CSV Columns (9 columns) The downloaded CSV has the following columns, in order: | Column | Type | Description | | ---------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `Path ID` | string | Unique identifier of the node in the path tree. | | `Parent Path ID` | string | `Path ID` of the parent node. Empty only for the single entry-point root (`Node Type = ENTRY_POINT`). | | `Script ID` | string | Script identifier. Empty for the entry-point node. | | `Script Name` | string | Human-readable script name. | | `Node Type` | string | Node kind. `ENTRY_POINT` is the root of the path tree (one per report); `SCRIPT` denotes an IVR script reached; other values are the IVR node type as defined in the IVR script (e.g. `TextSay`, `ForwardToQueue`, `GetDigit`, `Callback`), the corresponding node exit point (suffix `_ExitPoint`, e.g. `ForwardToQueue_ExitPoint`), or a terminal category such as `CustomerHangUp` or `AgentAccepted`. | | `Node Label` | string | Human-readable label of the node, as defined in the IVR script. May be empty for nodes that have no assigned label (e.g. `ENTRY_POINT`, `CustomerHangUp`). | | `Depth` | integer | Nesting depth in the path tree. `0` at the entry point. | | `Count` | integer | Number of interactions that traversed this node during the reporting window. | | `Terminal` | boolean | `true` when the node is a leaf in the path tree — no child nodes followed it in the data. Marks the end of an IVR path taken by at least one interaction. |
### 9.4. Reconstructing the Path Tree The CSV is flat but represents a tree. Each row has a `Path ID` (this node) and `Parent Path ID` (its parent). To rebuild the tree, index rows by `Path ID`, group children by `Parent Path ID`, then walk from the row whose `Parent Path ID` is empty (the `ENTRY_POINT`). ```text nodes = map() // "Path ID" -> row children = map(default: empty list) // "Parent Path ID" -> [ "Path ID", ... ] root = null for each row in csv: nodes[row["Path ID"]] = row if row["Parent Path ID"] is empty: root = row["Path ID"] else: children[row["Parent Path ID"]].append(row["Path ID"]) function walk(pathId, depth): node = nodes[pathId] print indent(depth), node["Node Type"], "—", node["Node Label"], "(count=", node["Count"], ", terminal=", node["Terminal"], ")" for each childId in children[pathId]: walk(childId, depth + 1) walk(root, 0) ``` --- ## CC Realtime Statistics > 📘 **You will need a working API key to begin** > > [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > The base URL is region specific, based on the location of your Contact Center tenant. * United States: `https://api.8x8.com/analytics/cc/{version}/realtime-metrics/` * Europe: `https://api.8x8.com/eu/analytics/cc/{version}/realtime-metrics/` * Asia-Pacific: `https://api.8x8.com/au/analytics/cc/{version}/realtime-metrics/` * Canada: `https://api.8x8.com/ca/analytics/cc/{version}/realtime-metrics/` * {version} to be replaced by current Version. As of October 2022 this is 5 resulting in /v5/ ## 1. Authenticate to retrieve access token [OAuth Authentication for 8x8 XCaaS APIs](/analytics/docs/oauth-authentication-for-8x8-xcaas-apis) is used to get a temporary `access_token` for use in with this API **Outputs For Next Step:** * access_token * expires_in The following steps will use the access_token as a Bearer Token form of authentication. This takes the form of the `Authorization` header being set to `Bearer access_token` (Space between Bearer and the access_token) ## 2 Multitenancy support If the API is used for a multitenant customer the requests should contain *"X-Tenant-Info"* header variable where needs to specify the desired tenantId. The "X-Tenant-Info" header is not mandatory in case of a single tenant customer. The following error messages could be returned when dealing with a multitenant customer: * if for a multitenant customer request the *"X-Tenant-Info"* header is not provided the HTTP 400 code along with *"Bad request: X-Tenant-Info header is missing."* message will be returned * if a wrong tenantId is provided the HTTP 400 code along with *"Bad request: Invalid value for X-Tenant-Info header."* message will be returned ## 3. Available Data Realtime data is available as follows. Definitions for metrics can be found in the [Metrics Glossary](#5-metrics-glossary) section below. * Queue Statistics ([Glossary](#51-queue-metrics-glossary)) * for multiple queues * for single queue * Agents Statistics * by Queue ([Glossary](#53-agent-metrics-glossary---queue-context)) * by multiple Queues ([Glossary](#56-agent-metrics-glossary---multiple-queues-context)) * by Group ([Glossary](#54-agent-metrics-glossary---group-context)) * all Agents ([Glossary](#55-agent-metrics-glossary---all-agents)) * Group Statistics ([Glossary](#52-group-metrics-glossary)) * for multiple groups * for single group ## 4. Accessing Realtime Queue Metrics > 📘 **Sample is for a multiple queues** > > For a single queue add /{queue-id} to the url. > > See [additional endpoints](/analytics/docs/cc-realtime-statistics#additional-endpoints) for examples for the other endpoints > > ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version is `v<\>` | v5 | | queue-id | ☐ | If a queue id is specified as a path parameter this limits the response to a single queue and queue-ids query parameter is ignored. Sample /queues/{queue-id} | /103 | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | page | ☐ | Page of the result set to return. Begins at zero (0). Default is zero. See [pagination](/analytics/docs/cc-realtime-statistics#pagination) for more details | 0 | | size | ☐ | The number of records per page to return. Default is 100. See [pagination](/analytics/docs/cc-realtime-statistics#pagination) for more details | 100 | | queue-ids | ☐ | When not passed all queues are returned. Specifies the queue-ids of the queues to be returned. For multiple queues add multiple times. `&queue-ids=101&queue-ids=107`. Only valid queue-ids can be passed. Passing an invalid queue-id will result in a failure. Ignored if /queue-id is passed as a path parameter. See [passing multiple ids](/analytics/docs/cc-realtime-statistics#passing-multiple-ids) for more information | 103 | | metrics | ✓ | When not passed all metrics are returned. Specifies the metrics to return. For multiple metrics add mutiple times. &metrics=handling.rt&metrics=entered.today. See [metrics](/analytics/docs/cc-realtime-statistics#metrics) for more details | | | timezone | ☐ | Only applies to .today metrics. The desired timezone ([IANA Time Zones](https://www.iana.org/time-zones). Examples America/New_York, Europe/Helsinki [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones)). Accepted timezone values are those that are configured for the tenant. The value can be the tenant’s default timezone or a value defined as an optional timezone. If no value is specified, the tenant’s default timezone is used | Europe/Helsinki | | summary | ☐ | Default false. **If set to true queue-ids and metrics parameters are ignored** and a list of queue-id and queue-name is returned | false | | includeTotals | ☐ | If this parameter is set to `true`, at the end of each sections with the totals for time, percentage, average and count metric type will be added at the end of sections in the response. Default false. See [includeTotals](/analytics/docs/cc-realtime-statistics#includetotals) for more information. | false | #### Metrics To get a full list of metrics for one of the endpoints call it without the metrics parameter. This will list all available metrics. Generally it's easier to specify the metrics you wish returned to have a more compact response. Realtime metrics are a snapshot of the current condition of the contact center. The naming convention of the metrics is aaa.bbb where aaa is the meric and bbb is the period of the metric. #### Periods explained * ".rt" - means realtime meaning currently. Example: there are currently X calls waiting in queue Summary metrics are over one of 3 provided ranges * ".int-15m" means in the most recent 15 minute interval. * ".int-30m" means in the most recent 30 minute interval * ".today" - is for the calendar day. X calls have entered the queue today. #### pagination page starts with zero (0). The maximum page size is 1000 if you specify a `size` above 1000 you will receive **400 Bad Request** ```json { "message": "Bad request: Field 'size' must be less than or equal to 1000" } ``` Responses will include the following headers related to pagination | Header | Description | Example | | --- | --- | --- | | X-Page-Size | Page size requested, default 100 | 100 | | X-Page | Current Page | 0 | | X-Total-Pages | Count of all pages, remember `page` parameter and X-Page are zero based so if X-Total-Pages is 13 the last page is = 12 | 12 | | X-Total-Elements | Total number of records | 1223 | If a page beyond the end of the result set is specified the response will be as follows HTTP STATUS : 400 Beyond last page ```json { "message": "Bad request: Field 'page' must be greater than or equal to 0 and less than the total number of pages, which is 2 for this request" } ``` ### Realtime Data Request In this example the data returned will only be for queue id is 103 and 170 and the only metrics returned will be handling.rt and metrics=entered.today queuesgroups ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v<\>/realtime-metrics/queues?page=0&size=100&queue-ids=101&queue-ids=107&metrics=handling.rt&metrics=entered.today' \ --header 'Authorization: Bearer FnZGG0u5BpNwRkuwKuSmfG2JAG9w' ``` ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/vv<\>/realtime-metrics/groups?page=0&size=100&group-ids=102&group-ids=1023&metrics=availableIdle.rt&metrics=enabled.rt' \ --header 'Authorization: Bearer FnZGG0u5BpNwRkuwKuSmfG2JAG9w' ``` ### Realtime Data Response Note the response headers related to [pagination](/analytics/docs/cc-realtime-statistics#pagination) above. #### Body queuesgroups ```json [ { "id": "103", "name": "Appointments", "metrics": [ { "key": "entered.today", "value": 7 }, { "key": "handling.rt", "value": 0 } ] }, { "id": "107", "name": "Tickets", "metrics": [ { "key": "entered.today", "value": 6 }, { "key": "handling.rt", "value": 0 } ] } ] ``` ```json [ { "id": "102", "name": "Sales", "metrics": [ { "key": "availableIdle.rt", "value": "0" }, { "key": "enabled.rt", "value": "0" } ] }, { "id": "1023", "name": "Deliveries", "metrics": [ { "key": "availableIdle.rt", "value": "0" }, { "key": "enabled.rt", "value": "0" } ] } ] ``` ## Additional Endpoints > 📘 **Core Parameters and structure are common** > > The following examples don't show every parameter refer to [parameters](/analytics/docs/cc-realtime-statistics#parameters) above. > > Metrics, timezone etc. are all available on each endpoint > > The core parameters and structure are common to all of the following endpoints. Which allow for selecting specific queue, groups and agents. The available metrics for queues, groups, agents within queues and agents within groups vary. ### Single Queue `/realtime-metrics/queues/{queue-id}` #### Path Parameter | Name | Required | Description | Example | | --- | --- | --- | --- | | queue-id | ✓ | Single queue id to return data for | 103 | #### Single Queue Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v<\>/realtime-metrics/queues/101?&metrics=handling.rt&metrics=entered.today' \ --header 'Authorization: Bearer FnZGG0u5BpNwRkuwKuSmfG2JAG9w' ``` #### Single Queue Response ```json [ { "id": "103", "name": "Appointments", "metrics": [ { "key": "entered.today", "value": 7 }, { "key": "handling.rt", "value": 0 } ] } ] ``` ### Agents within a Queue `/realtime-metrics/queues/{queue-id}/agents` #### Path Parameter | Name | Required | Description | Example | | --- | --- | --- | --- | | queue-id | ✓ | Single queue id to return agents for | 103 | #### Query Parameter `agent-ids` can be passed similarly to queue-ids in the prior example. | Name | Required | Description | Example | | --- | --- | --- | --- | | agent-ids | ☐ | When not passed all agents are returned. Specifies the agent-ids of the agents to be returned. For multiple agents add multiple times. `&agent-ids=agfAl1ZjIyQ8ecpoCB9KUbbb&agent-ids=agk4tyf8vnSMWki8r4e0dfff`. Only valid agent-ids can be passed. Passing an invalid agent-id will result in a failure. | 103 | #### Agents within a Queue Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v<\>/realtime-metrics/queues/{queue-id}/agents?page=0&size=100&agent-ids={agent-id1}&agent-ids={agent-id2}&metrics=status.rt,statusCode.rt,timeOnStatus.rt,timeOnStatusCode.rt,lastLogin.rt,lastStatusChange.rt,lastStatusCodeChange.rt' \ --header 'Authorization: Bearer FnZGG0u5BpNwRkuwKuSmfG2JAG9w' ``` #### Agents within a Queue Response ```json [ { "id": "agAD21EBR1RhuV2TNDivaaa", "name": "Jane Li", "metrics": [ { "key": "lastLogin.rt", "value": "2022-12-16T16:28:08.013Z" }, { "key": "lastStatusChange.rt", "value": "2022-12-16T18:15:22.741Z" }, { "key": "lastStatusCodeChange.rt", "value": "2022-12-16T18:15:22.741Z" }, { "key": "status.rt", "value": "LoggedOut" }, { "key": "statusCode.rt", "value": null }, { "key": "timeOnStatus.rt", "value": 245611272 }, { "key": "timeOnStatusCode.rt", "value": 145611272 } ] }, { "id": "agAQDqmvKiRbG4ekigNSbbbbb", "name": "James Woods", "metrics": [ { "key": "lastLogin.rt", "value": "2022-12-02T17:32:31.299Z" }, { "key": "lastStatusChange.rt", "value": "2022-12-02T19:45:18.632Z" }, { "key": "lastStatusCodeChange.rt", "value": "2022-12-02T19:45:18.632Z" }, { "key": "status.rt", "value": "Available" }, { "key": "statusCode.rt", "value": null }, { "key": "timeOnStatus.rt", "value": 1469499666 }, { "key": "timeOnStatusCode.rt", "value": 1269499666 } ] } ] ``` ### Single Agent within a Queue `/realtime-metrics/queues/{queue-id}/agents/{agent-id}` #### Path Parameters | Name | Required | Description | Example | | --- | --- | --- | --- | | queue-id | ✓ | A queue the agent is a member of | 103 | | agent-id | ✓ | The agent requested within the queue | agAD21EBR1RhuV2TNDivaaa | #### Single Agent within a Queue Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v<\>/realtime-metrics/queues/{queue-id}/agents/{agent-id}?metrics=status.rt,statusCode.rt,timeOnStatus.rt,timeOnStatusCode.rt,lastLogin.rt,lastStatusChange.rt,lastStatusCodeChange.rt' \ --header 'Authorization: Bearer FnZGG0u5BpNwRkuwKuSmfG2JAG9w' ``` #### Single Agent within a Queue Response ```json [ { "id": "agAD21EBR1RhuV2TNDivaaa", "name": "Jane Li", "metrics": [ { "key": "lastLogin.rt", "value": "2022-12-16T16:28:08.013Z" }, { "key": "lastStatusChange.rt", "value": "2022-12-16T18:15:22.741Z" }, { "key": "lastStatusCodeChange.rt", "value": "2022-12-16T18:15:22.741Z" }, { "key": "status.rt", "value": "LoggedOut" }, { "key": "statusCode.rt", "value": null }, { "key": "timeOnStatus.rt", "value": 245611272 }, { "key": "timeOnStatusCode.rt", "value": 145611272 } ] } ] ``` ### Agents within a Group of Queues > 🚧 **This endpoint ONLY returns information for agents/queues that have been had activity or a session in the current day.** > > `/realtime-metrics/agents-in-queue-groups` #### Query Parameter `queue-ids` can be passed similarly to queue-ids in the prior example. | Name | Required | Description | Example | | --- | --- | --- | --- | | queue-ids | ☐ | When not passed all queues are returned. Specifies the queue-ids of the queues to be returned. For multiple queues add multiple times. `&queue-ids=101&queue-ids=102`. Only valid queue-ids can be passed. Passing an invalid queue-id will result in a failure. | 103 | | showEnabledAgents | ☐ | When `true`, the response also includes agents assigned to the selected queues that are currently logged in (any status except `LoggedOut`), even if they have no current queue activity. These agents appear only in the top-level `agents` array with their full metrics; `queues[].agentMetrics` is not affected. Defaults to `false`. Available on v5 only. | true | When `showEnabledAgents=true`, an enabled-but-idle logged-in agent is added to the top-level `agents` array with the full metric set, while `queues[].agentMetrics` continues to list only agents with actual activity in that queue. `queues[].assignedAgents` is unchanged and already lists every agent assigned to the queue regardless of activity. Sample request limited to two queues and just three of the available metrics (Two agent related and one queue related) #### Agents within a Queue Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v<\>/realtime-metrics/agents-in-queue-groups?metrics=status.rt,timeOnStatus.rt,timeOnStatusCode.rt,accepted.today.inQueue&queue-ids={queue-id-1}&queue-ids={queue-id-2}&page=0&size=100' \ --header 'Authorization: Bearer FnZGG0u5BpNwRkuwKuSmfG2JAG9w' ``` The response contains two sections. First section is an array of agents, where for each agent the response contains a collection of the agent specific metrics. The second section is an array of queues where for each queue the response contains a collection of agents and a collection of queue specific metrics for each agent. Each agent will have in the response the agent ID and agent name. #### Agents within a Queue Response ```json { "agents": [ { "id": "aget4bO5y1SqqQ5HDDfaaaaa", "name": "John Agent", "metrics": { "status.rt": "Available", "timeOnStatus.rt": 123, "timeOnStatusCode.rt": 23 } }, { "id": "agwhzJ0NOwTdWid_JPaaaaa", "name": "Jane Agent", "metrics": { "status.rt": "LoggedOut", "timeOnStatus.rt": 2224, "timeOnStatusCode.rt": 224 } } ], "queues": [ { "queueId": "2943", "assignedAgents": [], "agentMetrics": [ { "id": "agwhzJ0NOwTdWid_JPaaaaa", "name": "Jane Agent", "metrics": { "accepted.today.inQueue": 1 } } ] }, { "queueId": "2046", "assignedAgents": [ "aget4bO5y1SqqQ5HDDfaaaaa", "agpQ5jHgnDTgq1lMu1qaOxzg" ], "agentMetrics": [ { "id": "aget4bO5y1SqqQ5HDDfaaaaa", "name": "John Agent", "metrics": { "accepted.today.inQueue": 2 } } ] } ] } ``` ### All agents at once within a tenant Retrieve all agents with all specified metrics values within a tenant. The *summary* parameter can be used to get only agent details without the metrics, in this case the *agent-ids*, *group-ids* and *metrics* parameters will be ignored. `/realtime-metrics/agents` #### Query Parameter | Name | Required | Description | Example | | --- | --- | --- | --- | | agent-ids | ☐ | Optional set of agent identifiers. Only metrics for these agents will be returned. | agLkndJlSOQReqFZI48LgyvQ | | group-ids | ☐ | Optional set of group identifiers. Only metrics for these groups will be returned. | 103 | | summary | ☐ | Returns a summary (id, name, group id, group name) of all agents. If this parameter is set as TRUE, agent-ids, group-ids and metrics parameters will be ignored. If this parameter is not set as TRUE, `metrics` parameter needs to be provided. | true | #### Request with a specified list of metrics, agent-ids and group-ids ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v5/realtime-metrics/agents?metrics=status.rt,timeOnStatus.rt,timeOnStatusCode.rt,accepted.today.inQueue&agent-ids={agent-id-1}&agent-ids={agent-id-2}&group-ids={group-id-1}&group-ids={group-id-2}&page=0&size=100' \ --header 'Authorization: Bearer FnZGG0u5BpNwRkuwKuSmfG2JAG9w' ``` #### Request with a summary parameter set to True ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v5/realtime-metrics/agents?summary=true&page=0&size=100' \ --header 'Authorization: Bearer FnZGG0u5BpNwRkuwKuSmfG2JAG9w' ``` #### All Agents within a tenant Response examples With *summary=true* parameter response example: ```json [ { "id": "ag0IWAMsLuSsijCkitWX76Ng", "name": "Agent 1", "groupId": "735", "groupName": "Nicu Group" }, { "id": "ag0OlQp4skQ5mOQsPvPRXDYw", "name": "Admin 1", "groupId": "100", "groupName": "ungroup" }, { "id": "ag10000", "name": "Admin 2", "groupId": "131", "groupName": "adi_group" } ] ``` With *summary=false* (or missing parameter) response example: ```json [ { "id": "ag0IWAMsLuSsijCkitWX76Ng", "name": "Agent 1", "metrics": [ { "key": "accepted.int-15m", "value": null }, { "key": "accepted.int-30m", "value": null }, { "key": "activeInteractionsCount.rt", "value": 3 }, { "key": "activeChannels.rt", "value": [ { "id": "t5mp0xxKQkKTqhI", "name": "Chat Channel 1", "count": 2 }, { "id": "k7V_0uMpSDCgnTlylSCScw", "name": "Chat O", "count": 1 } ] }, { "key": "activeDirections.rt", "value": [ { "id": "InboundDir", "name": "InboundDir", "count": 2 }, { "id": "OutboundDir", "name": "OutboundDir", "count": 1 } ] }, { "key": "activeQueues.rt", "value": [ { "id": "101", "name": "Inbound Queue 1", "count": 2 }, { "id": "102", "name": "Outbound Queue 1", "count": 1 } ] } ] }, { "id": "ag0OlQp4skQ5mOQsPvPRXDYw", "name": "Agent 2", "metrics": [ { "key": "accepted.int-15m", "value": null }, { "key": "accepted.int-30m", "value": null }, { "key": "activeInteractionsCount.rt", "value": 1 }, { "key": "activeChannels.rt", "value": [ { "id": "VphYLcHogw", "name": "Chat Channel 1", "count": 1 } ] }, { "key": "activeDirections.rt", "value": [ { "id": "InboundDir", "name": "InboundDir", "count": 1 } ] }, { "key": "activeQueues.rt", "value": [ { "id": "103", "name": "Inbound Chat 1", "count": 1 } ] } ] }, { "id": "ag10000", "name": "Agent 3", "metrics": [ { "key": "accepted.int-15m", "value": null }, { "key": "accepted.int-30m", "value": null }, { "key": "activeInteractionsCount.rt", "value": 0 }, { "key": "activeChannels.rt", "value": [] }, { "key": "activeDirections.rt", "value": [] }, { "key": "activeQueues.rt", "value": [] } ] } ] ``` ### Groups `/realtime-metrics/groups` #### Query Parameter | Name | Required | Description | Example | | --- | --- | --- | --- | | group-ids | ✓ | When not passed all groups are returned. Specifies the group-ids of the groups to be returned. For multiple groups add multiple times. `&group-ids=102&group-ids=1023`. Only valid group-ids can be passed. Passing an invalid group-id will result in a failure. | 103 | #### Groups Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v<\>/realtime-metrics/groups?size=100&page=0&group-ids=102&group-ids=1023&metrics=availableIdle.rt&metrics=enabled.rt' \ --header 'Authorization: Bearer FnZGG0u5BpNwRkuwKuSmfG2JAG9w' ``` #### Groups Response ```json [ { "id": "102", "name": "Sales", "metrics": [ { "key": "availableIdle.rt", "value": "0" }, { "key": "enabled.rt", "value": "0" } ] }, { "id": "1023", "name": "Deliveries", "metrics": [ { "key": "availableIdle.rt", "value": "0" }, { "key": "enabled.rt", "value": "0" } ] } ] ``` ### Single Group `/realtime-metrics/groups/{group-id}` #### Path Parameter | Name | Required | Description | Example | | --- | --- | --- | --- | | group-id | ✓ | Group Id of the requested group | 103 | #### Single Group Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v<\>/realtime-metrics/groups/{group-id}?metrics=availableIdle.rt&metrics=enabled.rt' \ --header 'Authorization: Bearer FnZGG0u5BpNwRkuwKuSmfG2JAG9w' ``` #### Groups Response ```json [ { "id": "102", "name": "Sales", "metrics": [ { "key": "availableIdle.rt", "value": "0" }, { "key": "enabled.rt", "value": "0" } ] } ] ``` ### Agents within a Group `/realtime-metrics/groups/{group-id}/agents` #### Path Parameter | Name | Required | Description | Example | | --- | --- | --- | --- | | group-id | ✓ | Group Id of the requested group | 103 | #### Query Parameter | Name | Required | Description | Example | | --- | --- | --- | --- | | agent-ids | ☐ | When not passed all agents are returned. Specifies the agent-ids of the agents to be returned. For multiple agents add multiple times. `&agent-ids=agfAl1ZjIyQ8ecpoCB9KUbbb&agent-ids=agk4tyf8vnSMWki8r4e0dfff`. Only valid agent-ids can be passed. Passing an invalid agent-id will result in a failure. | agk4tyf8vnSMWki8r4e0dfff | #### Agents within a Group Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v<\>/realtime-metrics/groups/{group-id}/agents?page=0&size=100&agent-ids={agent-id1}&agent-ids={agent-id2}&metrics=offered.today&metrics=status.rt' \ --header 'Authorization: Bearer FnZGG0u5BpNwRkuwKuSmfG2JAG9w' ``` #### Agents within a Group Response ```json [ { "id": "agAD21EBR1RhuV2TNDivaaa", "name": "Jane Li", "metrics": [ { "key": "offered.today", "value": 10 }, { "key": "status.rt", "value": "Available" } ] }, { "id": "agAQDqmvKiRbG4ekigNSbbbbb", "name": "James Woods", "metrics": [ { "key": "offered.today", "value": null }, { "key": "status.rt", "value": "LoggedOut" } ] } ] ``` ### Single Agent within a Group `/realtime-metrics/groups/{group-id}/agents/{agent-id}` #### Path Parameter | Name | Required | Description | Example | | --- | --- | --- | --- | | group-id | ✓ | Group Id of the requested group | 103 | | agent-id | ✓ | AgentId of the requested agent | agAD21EBR1RhuV2TNDivaaa | #### Single Agent within a Group Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v<\>/realtime-metrics/groups/{group-id}/agents/{agent-id}?metrics=offered.today&metrics=status.rt' \ --header 'Authorization: Bearer FnZGG0u5BpNwRkuwKuSmfG2JAG9w' ``` #### Single Agent within a Group Response ```json [ { "id": "agAD21EBR1RhuV2TNDivaaa", "name": "Jane Li", "metrics": [ { "key": "offered.today", "value": 10 }, { "key": "status.rt", "value": "Available" } ] } ] ``` ## Additional Information #### includeTotals When includeTotals is set to `true` an additional set of "total" metrics will be included for time, percentage, average and count metric types as follows. ```json { "id": null, "name": "totals", "metrics": [ { "key": "total.abandoned.int-15m", "value": 0 }, { "key": "total.abandoned.int-30m", "value": 0 }, { "key": "total.abandoned.today", "value": 0 }, { "key": "total.abandonedPercentage.int-15m", "value": null }, { "key": "total.abandonedPercentage.int-30m", "value": null }, { "key": "total.abandonedPercentage.today", "value": 0.0 }, { "key": "total.accepted.int-15m", "value": 0 }, { "key": "total.accepted.int-30m", "value": 0 }, { "key": "total.accepted.today", "value": 0 }, { "key": "total.acceptedInSla.int-12h", "value": 0 }, { "key": "total.acceptedInSla.int-15m", "value": 0 }, { "key": "total.acceptedInSla.int-1h", "value": 0 }, { "key": "total.acceptedInSla.int-30m", "value": 0 }, { "key": "total.acceptedInSla.int-4h", "value": 0 }, { "key": "total.acceptedInSla.int-8h", "value": 0 }, { "key": "total.acceptedInSla.today", "value": 0 }, { "key": "total.acceptedInSlaPercentage.int-12h", "value": null }, { "key": "total.acceptedInSlaPercentage.int-15m", "value": null }, { "key": "total.acceptedInSlaPercentage.int-1h", "value": null }, { "key": "total.acceptedInSlaPercentage.int-30m", "value": null }, { "key": "total.acceptedInSlaPercentage.int-4h", "value": null }, { "key": "total.acceptedInSlaPercentage.int-8h", "value": null }, { "key": "total.acceptedInSlaPercentage.today", "value": null }, { "key": "total.acceptedPercentage.int-15m", "value": null }, { "key": "total.acceptedPercentage.int-30m", "value": null }, { "key": "total.acceptedPercentage.today", "value": 0.0 }, { "key": "total.availableIdle.rt", "value": 0 }, { "key": "total.avgDivertedTime.int-15m", "value": null }, { "key": "total.avgDivertedTime.int-30m", "value": null }, { "key": "total.avgDivertedTime.today", "value": 29947.4 }, { "key": "total.avgHandlingTime.int-15m", "value": null }, { "key": "total.avgHandlingTime.int-30m", "value": null }, { "key": "total.avgHandlingTime.today", "value": null }, { "key": "total.avgOfferingTime.int-15m", "value": null }, { "key": "total.avgOfferingTime.int-30m", "value": null }, { "key": "total.avgOfferingTime.today", "value": null }, { "key": "total.avgProcessingTime.int-15m", "value": null }, { "key": "total.avgProcessingTime.int-30m", "value": null }, { "key": "total.avgProcessingTime.today", "value": null }, { "key": "total.avgWorkTime.int-15m", "value": null }, { "key": "total.avgWorkTime.int-30m", "value": null }, { "key": "total.avgWorkTime.today", "value": null }, { "key": "total.avgWrapUpTime.int-15m", "value": null }, { "key": "total.avgWrapUpTime.int-30m", "value": null }, { "key": "total.avgWrapUpTime.today", "value": null }, { "key": "total.busy.rt", "value": 0 }, { "key": "total.busyExternal.rt", "value": 0 }, { "key": "total.busyOther.rt", "value": 0 }, { "key": "total.diverted.int-15m", "value": 0 }, { "key": "total.diverted.int-30m", "value": 0 }, { "key": "total.diverted.today", "value": 5 }, { "key": "total.divertedPercentage.int-15m", "value": null }, { "key": "total.divertedPercentage.int-30m", "value": null }, { "key": "total.divertedPercentage.today", "value": 1.0 }, { "key": "total.eligible.rt", "value": 0 }, { "key": "total.enabled.rt", "value": 0 }, { "key": "total.entered.int-15m", "value": 0 }, { "key": "total.entered.int-30m", "value": 0 }, { "key": "total.entered.today", "value": 5 }, { "key": "total.handling.rt", "value": 0 }, { "key": "total.interactionsAvgWaitTime.int-15m", "value": null }, { "key": "total.interactionsAvgWaitTime.int-30m", "value": null }, { "key": "total.interactionsAvgWaitTime.today", "value": 29947.4 }, { "key": "total.interactionsHandling.rt", "value": 0 }, { "key": "total.interactionsLongestWaitInQueue.int-15m", "value": 0 }, { "key": "total.interactionsLongestWaitInQueue.int-30m", "value": 0 }, { "key": "total.interactionsLongestWaitInQueue.rt", "value": 0 }, { "key": "total.interactionsLongestWaitInQueue.today", "value": 29951 }, { "key": "total.interactionsWaitInQueue.rt", "value": 0 }, { "key": "total.interactionsWrapUp.rt", "value": 0 }, { "key": "total.longestOfferingTimeInQueue.int-15m", "value": 0 }, { "key": "total.longestOfferingTimeInQueue.int-30m", "value": 0 }, { "key": "total.longestOfferingTimeInQueue.today", "value": 0 }, { "key": "total.newInQueue.int-15m", "value": 0 }, { "key": "total.newInQueue.int-30m", "value": 0 }, { "key": "total.newInQueue.today", "value": 5 }, { "key": "total.offering.rt", "value": 0 }, { "key": "total.onBreak.rt", "value": 0 }, { "key": "total.shortAbandoned.int-15m", "value": 0 }, { "key": "total.shortAbandoned.int-30m", "value": 0 }, { "key": "total.shortAbandoned.today", "value": 0 }, { "key": "total.shortAbandonedPercentage.int-15m", "value": null }, { "key": "total.shortAbandonedPercentage.int-30m", "value": null }, { "key": "total.shortAbandonedPercentage.today", "value": 0.0 }, { "key": "total.slaPercentage.int-12h", "value": null }, { "key": "total.slaPercentage.int-15m", "value": null }, { "key": "total.slaPercentage.int-1h", "value": null }, { "key": "total.slaPercentage.int-30m", "value": null }, { "key": "total.slaPercentage.int-4h", "value": null }, { "key": "total.slaPercentage.int-8h", "value": null }, { "key": "total.slaPercentage.today", "value": null }, { "key": "total.totalAbandoned.int-15m", "value": 0 }, { "key": "total.totalAbandoned.int-30m", "value": 0 }, { "key": "total.totalAbandoned.today", "value": 0 }, { "key": "total.totalAbandonedPercentage.int-15m", "value": null }, { "key": "total.totalAbandonedPercentage.int-30m", "value": null }, { "key": "total.totalAbandonedPercentage.today", "value": 0.0 }, { "key": "total.workingOffline.rt", "value": 0 }, { "key": "total.wrapUp.rt", "value": 0 } ] } ``` #### Passing Multiple ids When passing multiple ids on endpoints with plural parameters such as agent-ids, queue-ids, group-ids. The parameters can be passed by repeating the parameter name: * `agent-ids=123&agent-ids-765&agent-ids=963` OR they can be passed as a comma separated list: * `agent-ids=123,765,963` ## 5. Metrics Glossary ### 5.1. Queue Metrics Glossary This glossary provides comprehensive definitions for all metrics available when querying queue statistics via the Real-time API. These metrics apply to the following endpoints: * `GET /queues` - All queues statistics * `GET /queues/{id}` - Individual queue statistics
Click to expand Queue Metrics Glossary (101 metrics) **Version** indicates minimum Real-time API version where metric became available on this API endpoint. | Metric | Version | Description | |--------|---------|-------------| | `abandoned.int-15m` | v1+ | Number of interactions customers ended while waiting in queue before reaching an agent, excluding short abandonments (last 15-minutes) | | `abandoned.int-30m` | v1+ | Number of interactions customers ended while waiting in queue before reaching an agent, excluding short abandonments (last 30-minutes) | | `abandoned.today` | v1+ | Number of interactions customers ended while waiting in queue before reaching an agent, excluding short abandonments (current day) | | `abandonedPercentage.int-15m` | v1+ | Percentage of interactions customers ended while waiting in queue before reaching an agent, excluding short abandonments, relative to total entries (last 15-minutes) | | `abandonedPercentage.int-30m` | v1+ | Percentage of interactions customers ended while waiting in queue before reaching an agent, excluding short abandonments, relative to total entries (last 30-minutes) | | `abandonedPercentage.today` | v1+ | Percentage of interactions customers ended while waiting in queue before reaching an agent, excluding short abandonments, relative to total entries (current day) | | `accepted.int-15m` | v1+ | Total interactions answered by agents. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (last 15-minutes) | | `accepted.int-30m` | v1+ | Total interactions answered by agents. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (last 30-minutes) | | `accepted.today` | v1+ | Total interactions answered by agents. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (current day) | | `acceptedInSla.int-12h` | v3+ | Total number of interactions answered by all agents within the SLA Threshold Time. Measures interactions where the agent answered before the configured SLA time limit was exceeded (last 12-hours) | | `acceptedInSla.int-15m` | v3+ | Total number of interactions answered by all agents within the SLA Threshold Time. Measures interactions where the agent answered before the configured SLA time limit was exceeded (last 15-minutes) | | `acceptedInSla.int-1h` | v3+ | Total number of interactions answered by all agents within the SLA Threshold Time. Measures interactions where the agent answered before the configured SLA time limit was exceeded (last 1-hour) | | `acceptedInSla.int-30m` | v3+ | Total number of interactions answered by all agents within the SLA Threshold Time. Measures interactions where the agent answered before the configured SLA time limit was exceeded (last 30-minutes) | | `acceptedInSla.int-4h` | v3+ | Total number of interactions answered by all agents within the SLA Threshold Time. Measures interactions where the agent answered before the configured SLA time limit was exceeded (last 4-hours) | | `acceptedInSla.int-8h` | v3+ | Total number of interactions answered by all agents within the SLA Threshold Time. Measures interactions where the agent answered before the configured SLA time limit was exceeded (last 8-hours) | | `acceptedInSla.today` | v3+ | Total number of interactions answered by all agents within the SLA Threshold Time. Measures interactions where the agent answered before the configured SLA time limit was exceeded (current day) | | `acceptedInSlaPercentage.int-12h` | v3+ | Percentage of total number of interactions answered by all agents within the SLA Threshold Time, relative to total accepted interactions (last 12-hours) | | `acceptedInSlaPercentage.int-15m` | v3+ | Percentage of total number of interactions answered by all agents within the SLA Threshold Time, relative to total accepted interactions (last 15-minutes) | | `acceptedInSlaPercentage.int-1h` | v3+ | Percentage of total number of interactions answered by all agents within the SLA Threshold Time, relative to total accepted interactions (last 1-hour) | | `acceptedInSlaPercentage.int-30m` | v3+ | Percentage of total number of interactions answered by all agents within the SLA Threshold Time, relative to total accepted interactions (last 30-minutes) | | `acceptedInSlaPercentage.int-4h` | v3+ | Percentage of total number of interactions answered by all agents within the SLA Threshold Time, relative to total accepted interactions (last 4-hours) | | `acceptedInSlaPercentage.int-8h` | v3+ | Percentage of total number of interactions answered by all agents within the SLA Threshold Time, relative to total accepted interactions (last 8-hours) | | `acceptedInSlaPercentage.today` | v3+ | Percentage of total number of interactions answered by all agents within the SLA Threshold Time, relative to total accepted interactions (current day) | | `acceptedPercentage.int-15m` | v1+ | Percentage of total interactions answered by agents relative to total entries. Shows what proportion of all interactions entering the queue were successfully connected to and handled by an agent (last 15-minutes) | | `acceptedPercentage.int-30m` | v1+ | Percentage of total interactions answered by agents relative to total entries. Shows what proportion of all interactions entering the queue were successfully connected to and handled by an agent (last 30-minutes) | | `acceptedPercentage.today` | v1+ | Percentage of total interactions answered by agents relative to total entries. Shows what proportion of all interactions entering the queue were successfully connected to and handled by an agent (current day) | | `availableIdle.rt` | v1+ | Agents currently enabled, assigned, and waiting in Available state for queue interactions. These are agents ready and waiting to receive the next incoming interaction (currently) | | `avgDivertedTime.int-15m` | v1+ | Average duration diverted interactions remain in queue before departure. Measures the average time spent by interactions in queue before that were transferred, forwarded or routed away via IVR (last 15-minutes) | | `avgDivertedTime.int-30m` | v1+ | Average duration diverted interactions remain in queue before departure. Measures the average time spent by interactions in queue before that were transferred, forwarded or routed away via IVR (last 30-minutes) | | `avgDivertedTime.today` | v1+ | Average duration diverted interactions remain in queue before departure. Measures the average time spent by interactions in queue before that were transferred, forwarded or routed away via IVR (current day) | | `avgHandlingTime.int-15m` | v1+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (last 15-minutes) | | `avgHandlingTime.int-30m` | v1+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (last 30-minutes) | | `avgHandlingTime.today` | v1+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (current day) | | `avgOfferingTime.int-15m` | v1+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (last 15-minutes) | | `avgOfferingTime.int-30m` | v1+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (last 30-minutes) | | `avgOfferingTime.today` | v1+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (current day) | | `avgProcessingTime.int-15m` | v1+ | Average combined time in Handling and Wrap Up states per accepted interaction. This represents the total time from when an agent accepts an interaction through final completion, including wrap-up work (last 15-minutes) | | `avgProcessingTime.int-30m` | v1+ | Average combined time in Handling and Wrap Up states per accepted interaction. This represents the total time from when an agent accepts an interaction through final completion, including wrap-up work (last 30-minutes) | | `avgProcessingTime.today` | v1+ | Average combined time in Handling and Wrap Up states per accepted interaction. This represents the total time from when an agent accepts an interaction through final completion, including wrap-up work (current day) | | `avgWorkTime.int-15m` | v1+ | Average combined time in Offering, Handling, and Wrap Up states per interaction. Includes the full duration from when an interaction is offered, through handling, until all work is complete (last 15-minutes) | | `avgWorkTime.int-30m` | v1+ | Average combined time in Offering, Handling, and Wrap Up states per interaction. Includes the full duration from when an interaction is offered, through handling, until all work is complete (last 30-minutes) | | `avgWorkTime.today` | v1+ | Average combined time in Offering, Handling, and Wrap Up states per interaction. Includes the full duration from when an interaction is offered, through handling, until all work is complete (current day) | | `avgWrapUpTime.int-15m` | v1+ | Average post-processing time per interaction entered. Time spent by agents completing administrative tasks after finishing handling an interaction (last 15-minutes) | | `avgWrapUpTime.int-30m` | v1+ | Average post-processing time per interaction entered. Time spent by agents completing administrative tasks after finishing handling an interaction (last 30-minutes) | | `avgWrapUpTime.today` | v1+ | Average post-processing time per interaction entered. Time spent by agents completing administrative tasks after finishing handling an interaction (current day) | | `busy.rt` | v1+ | Agents in Offering, Handling, or Wrap Up states actively working. Represents all agents who are actively working on interactions (currently) | | `busyExternal.rt` | v1+ | Interactions transferred from other queues handled by current queue agents. Shows interactions that originated in a different queue but are being serviced by agents in this queue (currently) | | `busyOther.rt` | v1+ | Agents working on interactions in different queues. Represents agents assigned to this queue but currently handling interactions for other queues (currently) | | `diverted.int-15m` | v1+ | Interactions leaving queue without termination via transfer, forwarding, or IVR routing. Represents interactions that were moved out of the queue through various routing mechanisms (last 15-minutes) | | `diverted.int-30m` | v1+ | Interactions leaving queue without termination via transfer, forwarding, or IVR routing. Represents interactions that were moved out of the queue through various routing mechanisms (last 30-minutes) | | `diverted.today` | v1+ | Interactions leaving queue without termination via transfer, forwarding, or IVR routing. Represents interactions that were moved out of the queue through various routing mechanisms (current day) | | `divertedPercentage.int-15m` | v1+ | Percentage of interactions leaving queue without termination via transfer, forwarding, or IVR routing, relative to total entries (last 15-minutes) | | `divertedPercentage.int-30m` | v1+ | Percentage of interactions leaving queue without termination via transfer, forwarding, or IVR routing, relative to total entries (last 30-minutes) | | `divertedPercentage.today` | v1+ | Percentage of interactions leaving queue without termination via transfer, forwarding, or IVR routing, relative to total entries (current day) | | `eligible.rt` | v1+ | Count of agents available to be offered interactions. Eligible agents are those not on break and capable of receiving interactions (currently) | | `enabled.rt` | v1+ | Count of agents logged in, assigned, and enabled for the specific queue. Includes all agents regardless of their current status (Available, Busy, Break, etc.) (currently) | | `entered.int-15m` | v1+ | Inbound interactions entering queue; outbound interactions directed through queue. Counts all interactions that came into this queue during the period (last 15-minutes) | | `entered.int-30m` | v1+ | Inbound interactions entering queue; outbound interactions directed through queue. Counts all interactions that came into this queue during the period (last 30-minutes) | | `entered.today` | v1+ | Inbound interactions entering queue; outbound interactions directed through queue. Counts all interactions that came into this queue during the period (current day) | | `handling.rt` | v1+ | Agents actively processing interactions, excluding Wrap Up state. Shows only agents currently in the Handling state (currently) | | `interactionsAvgWaitTime.int-15m` | v1+ | Average waiting time for interactions. Time interactions spend in queue from entry until acceptance, abandonment, or diversion (last 15-minutes) | | `interactionsAvgWaitTime.int-30m` | v1+ | Average waiting time for interactions. Time interactions spend in queue from entry until acceptance, abandonment, or diversion (last 30-minutes) | | `interactionsAvgWaitTime.today` | v1+ | Average waiting time for interactions. Time interactions spend in queue from entry until acceptance, abandonment, or diversion (current day) | | `interactionsHandling.rt` | v1+ | Currently active interactions being handled by agents. Represents the number of interactions where an agent is currently connected and processing (currently) | | `interactionsLongestWaitInQueue.int-15m` | v1+ | Longest wait in queue for interactions. Duration of the longest waiting interaction in the queue (last 15-minutes) | | `interactionsLongestWaitInQueue.int-30m` | v1+ | Longest wait in queue for interactions. Duration of the longest waiting interaction in the queue (last 30-minutes) | | `interactionsLongestWaitInQueue.rt` | v1+ | Longest wait in queue for interactions. Duration of the longest waiting interaction currently in the queue (currently) | | `interactionsLongestWaitInQueue.today` | v1+ | Longest wait in queue for interactions. Duration of the longest waiting interaction in the queue (current day) | | `interactionsWaitInQueue.rt` | v1+ | Number of interactions waiting in queue. Count of interactions currently queued and awaiting agent connection (currently) | | `interactionsWrapUp.rt` | v1+ | Number of interactions in Wrap Up state. Interactions disconnected from customers with agents completing post-interaction administrative work (currently) | | `longestOfferingTimeInQueue.int-15m` | v1+ | Maximum duration from offer to acceptance or rejection across interactions. Shows the longest time any interaction remained in Offering state during the period (last 15-minutes) | | `longestOfferingTimeInQueue.int-30m` | v1+ | Maximum duration from offer to acceptance or rejection across interactions. Shows the longest time any interaction remained in Offering state during the period (last 30-minutes) | | `longestOfferingTimeInQueue.today` | v1+ | Maximum duration from offer to acceptance or rejection across interactions. Shows the longest time any interaction remained in Offering state during the period (current day) | | `newInQueue.int-15m` | v4+ | Interactions entering queue within specified time interval only. Excludes interactions from previous intervals, showing only freshly entered interactions (last 15-minutes) | | `newInQueue.int-30m` | v4+ | Interactions entering queue within specified time interval only. Excludes interactions from previous intervals, showing only freshly entered interactions (last 30-minutes) | | `newInQueue.today` | v4+ | Interactions entering queue within specified time interval only. Excludes interactions from previous intervals, showing only freshly entered interactions (current day) | | `offering.rt` | v1+ | Number of agents in Offering state. Agents with interactions currently being offered awaiting acceptance or rejection (currently) | | `onBreak.rt` | v1+ | Enabled agents currently in On Break state. Shows agents logged in and assigned to this queue but temporarily unavailable due to break (currently) | | `shortAbandoned.int-15m` | v1+ | Number of short abandonments. Interactions ending with customer exit in queue before 5 seconds (last 15-minutes) | | `shortAbandoned.int-30m` | v1+ | Number of short abandonments. Interactions ending with customer exit in queue before 5 seconds (last 30-minutes) | | `shortAbandoned.today` | v1+ | Number of short abandonments. Interactions ending with customer exit in queue before 5 seconds (current day) | | `shortAbandonedPercentage.int-15m` | v1+ | Percentage of short abandonments. Percentage of interactions ending with customer exit in queue before 5 seconds relative to total entries (last 15-minutes) | | `shortAbandonedPercentage.int-30m` | v1+ | Percentage of short abandonments. Percentage of interactions ending with customer exit in queue before 5 seconds relative to total entries (last 30-minutes) | | `shortAbandonedPercentage.today` | v1+ | Percentage of short abandonments. Percentage of interactions ending with customer exit in queue before 5 seconds relative to total entries (current day) | | `slaPercentage.int-12h` | v1+ | Percentage of interactions answered before configured SLA time threshold relative to total entries, excluding short abandonments (last 12-hours) | | `slaPercentage.int-15m` | v1+ | Percentage of interactions answered before configured SLA time threshold relative to total entries, excluding short abandonments (last 15-minutes) | | `slaPercentage.int-1h` | v1+ | Percentage of interactions answered before configured SLA time threshold relative to total entries, excluding short abandonments (last 1-hour) | | `slaPercentage.int-30m` | v1+ | Percentage of interactions answered before configured SLA time threshold relative to total entries, excluding short abandonments (last 30-minutes) | | `slaPercentage.int-4h` | v1+ | Percentage of interactions answered before configured SLA time threshold relative to total entries, excluding short abandonments (last 4-hours) | | `slaPercentage.int-8h` | v1+ | Percentage of interactions answered before configured SLA time threshold relative to total entries, excluding short abandonments (last 8-hours) | | `slaPercentage.today` | v1+ | Percentage of interactions answered before configured SLA time threshold relative to total entries, excluding short abandonments (current day) | | `slaPercentageTarget.today` | v1+ | The target percentage of interactions that meet the SLA. Shows the goal set for this queue's service level performance | | `slaTimeThreshold.today` | v1+ | SLA time threshold. Time limit in milliseconds configured for acceptable interaction answering. Maximum duration from queue entry to agent answer for SLA compliance | | `totalAbandoned.int-15m` | v1+ | All interactions finishing in abandonment including short abandonments. Provides complete picture of both quick and extended abandonments combined (last 15-minutes) | | `totalAbandoned.int-30m` | v1+ | All interactions finishing in abandonment including short abandonments. Provides complete picture of both quick and extended abandonments combined (last 30-minutes) | | `totalAbandoned.today` | v1+ | All interactions finishing in abandonment including short abandonments. Provides complete picture of both quick and extended abandonments combined (current day) | | `totalAbandonedPercentage.int-15m` | v1+ | Percentage of abandoned interactions relative to total entries. Shows the complete abandonment rate including all types of abandonments (last 15-minutes) | | `totalAbandonedPercentage.int-30m` | v1+ | Percentage of abandoned interactions relative to total entries. Shows the complete abandonment rate including all types of abandonments (last 30-minutes) | | `totalAbandonedPercentage.today` | v1+ | Percentage of abandoned interactions relative to total entries. Shows the complete abandonment rate including all types of abandonments (current day) | | `workingOffline.rt` | v1+ | Enabled agents currently in Working Offline state. Represents agents who are logged in and working but not available to receive incoming interactions (currently) | | `wrapUp.rt` | v1+ | Number of agents in Wrap Up state. Agents completing post-interaction administrative work after finishing handling interactions (currently) |
### 5.2. Group Metrics Glossary This glossary provides comprehensive definitions for all metrics available when querying group statistics via the Real-time API. These metrics apply to the following endpoints: * `GET /realtime-metrics/groups` - All groups statistics * `GET /realtime-metrics/groups/{id}` - Individual group statistics
Click to expand Group Metrics Glossary (7 metrics) **Version** indicates minimum Real-time API version where metric became available on this API endpoint. | Metric | Version | Description | |--------|---------|-------------| | `availableIdle.rt` | v1+ | Agents belonging to the agent group who are currently enabled, assigned, and waiting in Available state for interactions. These are agents ready and waiting to receive the next incoming interaction (currently) | | `eligible.rt` | v1+ | Count of agents in the agent group available to be offered interactions. Eligible agents are those not on break and capable of receiving interactions (currently) | | `enabled.rt` | v1+ | Count of agents logged in, enabled, and belonging to the specific agent group. Includes all agents belonging to this group regardless of their current status (Available, Busy, Break, etc.) (currently) | | `handling.rt` | v1+ | Agents in the agent group actively processing interactions, excluding Wrap Up state. Shows only agents currently in the Handling state (currently) | | `onBreak.rt` | v1+ | Enabled agents in the agent group currently in On Break state. Shows agents logged in and belonging to this group but temporarily unavailable due to break (currently) | | `workingOffline.rt` | v1+ | Enabled agents in the agent group currently in Working Offline state. Represents agents who are logged in and working but not available to receive incoming interactions (currently) | | `wrapUp.rt` | v1+ | Number of agents in the agent group in Wrap Up state. Agents completing post-interaction administrative work after finishing handling interactions (currently) |
### 5.3. Agent Metrics Glossary - Queue Context This glossary provides comprehensive definitions for all metrics available when querying agent statistics within a queue context via the Real-time API. These metrics apply to the following endpoints: * `GET /realtime-metrics/queues/{queue-id}/agents` - All agents in a queue * `GET /realtime-metrics/queues/{queue-id}/agents/{agent-id}` - Individual agent in a queue **Note on `.inQueue` metrics:** The `.inQueue` suffix indicates that the metric measures agent activity specific to interactions routed through the **current queue**. These metrics exclude agent activity from direct inbound/outbound calls, internal agent-to-agent calls, and other non-queue interactions. Use `.inQueue` metrics to analyze agent performance for the selected queue.
Click to expand Agent Metrics Glossary - Queue Context (140 metrics) **Version** indicates minimum Real-time API version where metric became available on this API endpoint. | Metric | Version | Description | |------------------------------------------------|---------|-------------| | `accepted.int-15m.inQueue` | v1+ | Total interactions answered by the agent. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (last 15-minutes) | | `accepted.int-30m.inQueue` | v1+ | Total interactions answered by the agent. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (last 30-minutes) | | `accepted.today.inQueue` | v1+ | Total interactions answered by the agent. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (current day) | | `alerting.rt` | v1+ | Number of interactions from the current queue currently being presented to the agent, awaiting acceptance or rejection. Note that although this metric has no `.inQueue` suffix it is scoped to the current queue: it excludes internal agent-to-agent calls and chats, and interactions presented to the agent from other queues or by direct assignment (currently) | | `availableTime.int-15m` | v1+ | Total time the agent spent in Available state, ready to receive incoming interactions (last 15-minutes) | | `availableTime.int-30m` | v1+ | Total time the agent spent in Available state, ready to receive incoming interactions (last 30-minutes) | | `availableTime.today` | v1+ | Total time the agent spent in Available state, ready to receive incoming interactions (current day) | | `availableTimePercentage.int-15m` | v1+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work (last 15-minutes) | | `availableTimePercentage.int-30m` | v1+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work (last 30-minutes) | | `availableTimePercentage.today` | v1+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work (current day) | | `averageHandlingTime.int-15m.inQueue` | v1+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (last 15-minutes) | | `averageHandlingTime.int-30m.inQueue` | v1+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (last 30-minutes) | | `averageHandlingTime.today.inQueue` | v1+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (current day) | | `averageHoldTime.int-15m.inQueue` | v2+ | Average time the agent placed customers on hold (last 15-minutes) | | `averageHoldTime.int-30m.inQueue` | v2+ | Average time the agent placed customers on hold (last 30-minutes) | | `averageHoldTime.today.inQueue` | v2+ | Average time the agent placed customers on hold (current day) | | `averageOfferingTime.int-15m.inQueue` | v1+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (last 15-minutes) | | `averageOfferingTime.int-30m.inQueue` | v1+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (last 30-minutes) | | `averageOfferingTime.today.inQueue` | v1+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (current day) | | `averageWrapUpTime.int-15m.inQueue` | v1+ | Average post-processing time. Time spent by agents completing administrative tasks after finishing handling an interaction (last 15-minutes) | | `averageWrapUpTime.int-30m.inQueue` | v1+ | Average post-processing time. Time spent by agents completing administrative tasks after finishing handling an interaction (last 30-minutes) | | `averageWrapUpTime.today.inQueue` | v1+ | Average post-processing time. Time spent by agents completing administrative tasks after finishing handling an interaction (current day) | | `blindTransfers.int-15m.inQueue` | v1+ | Number of blind transfers performed by the agent. Transfer where agent does not speak to recipient first (last 15-minutes) | | `blindTransfers.int-30m.inQueue` | v1+ | Number of blind transfers performed by the agent. Transfer where agent does not speak to recipient first (last 30-minutes) | | `blindTransfers.today.inQueue` | v1+ | Number of blind transfers performed by the agent. Transfer where agent does not speak to recipient first (current day) | | `busyTime.int-15m` | v1+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities (last 15-minutes) | | `busyTime.int-30m` | v1+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities (last 30-minutes) | | `busyTime.today` | v1+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities (current day) | | `busyTimePercentage.int-15m` | v1+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working (last 15-minutes) | | `busyTimePercentage.int-30m` | v1+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working (last 30-minutes) | | `busyTimePercentage.today` | v1+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working (current day) | | `conferenceTime.int-15m.inQueue` | v1+ | Total cumulative duration the agent spent in multi-party conference calls (last 15-minutes) | | `conferenceTime.int-30m.inQueue` | v1+ | Total cumulative duration the agent spent in multi-party conference calls (last 30-minutes) | | `conferenceTime.today.inQueue` | v1+ | Total cumulative duration the agent spent in multi-party conference calls (current day) | | `conferences.int-15m.inQueue` | v1+ | Total number of conferences established by the agent (last 15-minutes) | | `conferences.int-30m.inQueue` | v1+ | Total number of conferences established by the agent (last 30-minutes) | | `conferences.today.inQueue` | v1+ | Total number of conferences established by the agent (current day) | | `consultations.int-15m.inQueue` | v1+ | Times an agent successfully established an outbound call while another call is on hold (last 15-minutes) | | `consultations.int-30m.inQueue` | v1+ | Times an agent successfully established an outbound call while another call is on hold (last 30-minutes) | | `consultations.today.inQueue` | v1+ | Times an agent successfully established an outbound call while another call is on hold (current day) | | `directInboundTime.int-15m` | v1+ | Total cumulative duration the agent spent on direct inbound calls, excluding agent-to-agent calls (last 15-minutes) | | `directInboundTime.int-30m` | v1+ | Total cumulative duration the agent spent on direct inbound calls, excluding agent-to-agent calls (last 30-minutes) | | `directInboundTime.today` | v1+ | Total cumulative duration the agent spent on direct inbound calls, excluding agent-to-agent calls (current day) | | `directInbounds.int-15m` | v1+ | Total number of direct inbound calls to the agent excluding agent-to-agent calls (last 15-minutes) | | `directInbounds.int-30m` | v1+ | Total number of direct inbound calls to the agent excluding agent-to-agent calls (last 30-minutes) | | `directInbounds.today` | v1+ | Total number of direct inbound calls to the agent excluding agent-to-agent calls (current day) | | `directOutboundTime.int-15m` | v1+ | Total cumulative duration the agent spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls (last 15-minutes) | | `directOutboundTime.int-30m` | v1+ | Total cumulative duration the agent spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls (last 30-minutes) | | `directOutboundTime.today` | v1+ | Total cumulative duration the agent spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls (current day) | | `directOutbounds.int-15m` | v1+ | Number of calls made by the agent excluding outbound queue calls and agent-to-agent calls (last 15-minutes) | | `directOutbounds.int-30m` | v1+ | Number of calls made by the agent excluding outbound queue calls and agent-to-agent calls (last 30-minutes) | | `directOutbounds.today` | v1+ | Number of calls made by the agent excluding outbound queue calls and agent-to-agent calls (current day) | | `handlingTime.int-15m` | v1+ | Total time the agent spent in Handling state, actively processing interactions (last 15-minutes) | | `handlingTime.int-30m` | v1+ | Total time the agent spent in Handling state, actively processing interactions (last 30-minutes) | | `handlingTime.today` | v1+ | Total time the agent spent in Handling state, actively processing interactions (current day) | | `handlingTimePercentage.int-15m` | v1+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions (last 15-minutes) | | `handlingTimePercentage.int-30m` | v1+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions (last 30-minutes) | | `handlingTimePercentage.today` | v1+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions (current day) | | `hold.int-15m.inQueue` | v1+ | Number of occasions the agent placed customers on hold (last 15-minutes) | | `hold.int-30m.inQueue` | v1+ | Number of occasions the agent placed customers on hold (last 30-minutes) | | `hold.today.inQueue` | v1+ | Number of occasions the agent placed customers on hold (current day) | | `internalCalls.int-15m` | v1+ | Total number of agent-to-agent calls initiated or received by the agent (last 15-minutes) | | `internalCalls.int-30m` | v1+ | Total number of agent-to-agent calls initiated or received by the agent (last 30-minutes) | | `internalCalls.today` | v1+ | Total number of agent-to-agent calls initiated or received by the agent (current day) | | `internalCallsInitiated.int-15m` | v1+ | Number of agent-to-agent calls initiated by this agent (last 15-minutes) | | `internalCallsInitiated.int-30m` | v1+ | Number of agent-to-agent calls initiated by this agent (last 30-minutes) | | `internalCallsInitiated.today` | v1+ | Number of agent-to-agent calls initiated by this agent (current day) | | `internalCallsReceived.int-15m` | v1+ | Number of agent-to-agent calls received by this agent (last 15-minutes) | | `internalCallsReceived.int-30m` | v1+ | Number of agent-to-agent calls received by this agent (last 30-minutes) | | `internalCallsReceived.today` | v1+ | Number of agent-to-agent calls received by this agent (current day) | | `internalCallsTime.int-15m` | v1+ | Total cumulative duration the agent spent on agent-to-agent calls, both initiated and received (last 15-minutes) | | `internalCallsTime.int-30m` | v1+ | Total cumulative duration the agent spent on agent-to-agent calls, both initiated and received (last 30-minutes) | | `internalCallsTime.today` | v1+ | Total cumulative duration the agent spent on agent-to-agent calls, both initiated and received (current day) | | `lastLogin.rt` | v1+ | Timestamp representing the agent's most recent login to the system (currently) | | `lastLogout.rt` | v1+ | Timestamp representing the agent's most recent logout from the system (currently) | | `lastStatusChange.rt` | v5+ | Timestamp when the agent's operational status last changed (currently) | | `lastStatusCodeChange.rt` | v5+ | Timestamp when the agent's status code last changed (currently) | | `line1Status.rt` | v1+ | Current operational status of the agent's first communication line (currently) | | `line1TimeOnStatus.rt` | v1+ | Time in milliseconds since the agent's first line status last changed (currently) | | `line2Status.rt` | v1+ | Current operational status of the agent's second communication line (currently) | | `line2TimeOnStatus.rt` | v1+ | Time in milliseconds since the agent's second line status last changed (currently) | | `loggedInTime.int-15m` | v1+ | Total time the agent maintained active system connection and was available for work across all queues and activities (last 15-minutes) | | `loggedInTime.int-30m` | v1+ | Total time the agent maintained active system connection and was available for work across all queues and activities (last 30-minutes) | | `loggedInTime.rt` | v1+ | Cumulative time the agent has maintained active system connection during their current login session (currently) | | `loggedInTime.today` | v1+ | Total time the agent maintained active system connection and was available for work across all queues and activities (current day) | | `longestHold.int-15m.inQueue` | v1+ | Maximum single continuous hold duration when agent placed customer on hold (last 15-minutes) | | `longestHold.int-30m.inQueue` | v1+ | Maximum single continuous hold duration when agent placed customer on hold (last 30-minutes) | | `longestHold.today.inQueue` | v1+ | Maximum single continuous hold duration when agent placed customer on hold (current day) | | `longestOffering.int-15m.inQueue` | v1+ | Maximum duration from when interaction was offered until agent accepted or rejected it. Shows longest time interaction remained in Offering state (last 15-minutes) | | `longestOffering.int-30m.inQueue` | v1+ | Maximum duration from when interaction was offered until agent accepted or rejected it. Shows longest time interaction remained in Offering state (last 30-minutes) | | `longestOffering.today.inQueue` | v1+ | Maximum duration from when interaction was offered until agent accepted or rejected it. Shows longest time interaction remained in Offering state (current day) | | `offered.int-15m.inQueue` | v1+ | Total interactions presented to the agent for acceptance or rejection. Includes interactions continuing from prior intervals (last 15-minutes) | | `offered.int-30m.inQueue` | v1+ | Total interactions presented to the agent for acceptance or rejection. Includes interactions continuing from prior intervals (last 30-minutes) | | `offered.today.inQueue` | v1+ | Total interactions presented to the agent for acceptance or rejection. Includes interactions continuing from prior intervals (current day) | | `offeringTime.int-15m` | v1+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities (last 15-minutes) | | `offeringTime.int-30m` | v1+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities (last 30-minutes) | | `offeringTime.today` | v1+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities (current day) | | `onBreakTime.int-15m` | v1+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions (last 15-minutes) | | `onBreakTime.int-30m` | v1+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions (last 30-minutes) | | `onBreakTime.today` | v1+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions (current day) | | `onBreakTimePercentage.int-15m` | v1+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break (last 15-minutes) | | `onBreakTimePercentage.int-30m` | v1+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break (last 30-minutes) | | `onBreakTimePercentage.today` | v1+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break (current day) | | `onHoldTime.int-15m.inQueue` | v1+ | Total duration the agent kept customers on hold. Sum of all hold periods (last 15-minutes) | | `onHoldTime.int-30m.inQueue` | v1+ | Total duration the agent kept customers on hold. Sum of all hold periods (last 30-minutes) | | `onHoldTime.today.inQueue` | v1+ | Total duration the agent kept customers on hold. Sum of all hold periods (current day) | | `rejectTimeout.int-15m.inQueue` | v1+ | Count of interactions automatically rejected when agent did not respond within configured timeout period (last 15-minutes) | | `rejectTimeout.int-30m.inQueue` | v1+ | Count of interactions automatically rejected when agent did not respond within configured timeout period (last 30-minutes) | | `rejectTimeout.today.inQueue` | v1+ | Count of interactions automatically rejected when agent did not respond within configured timeout period (current day) | | `rejected.int-15m.inQueue` | v1+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer (last 15-minutes) | | `rejected.int-30m.inQueue` | v1+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer (last 30-minutes) | | `rejected.today.inQueue` | v1+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer (current day) | | `status.rt` | v1+ | Agent's current operational state showing system status. Examples: Available, Handling, OnBreak, LoggedOut, WorkingOffline (currently) | | `statusCode.rt` | v1+ | Specific reason code that justifies or details the agent's current operational status (currently) | | `timeOnStatus.rt` | v1+ | Elapsed duration in milliseconds showing how long the agent has maintained their current operational status (currently) | | `timeOnStatusCode.rt` | v5+ | Elapsed duration in milliseconds showing how long the agent has maintained their current status code. The metric resets every time the status or the combination status + statusCode changes | | `transfersInitiated.int-15m.inQueue` | v2+ | Warm and blind transfers initiated by the agent. All outgoing transfers (last 15-minutes) | | `transfersInitiated.int-30m.inQueue` | v2+ | Warm and blind transfers initiated by the agent. All outgoing transfers (last 30-minutes) | | `transfersInitiated.today.inQueue` | v2+ | Warm and blind transfers initiated by the agent. All outgoing transfers (current day) | | `transfersInitiatedPercentage.int-15m.inQueue` | v2+ | Percentage of interactions transferred by the agent, calculated relative to total interactions accepted (last 15-minutes) | | `transfersInitiatedPercentage.int-30m.inQueue` | v2+ | Percentage of interactions transferred by the agent, calculated relative to total interactions accepted (last 30-minutes) | | `transfersInitiatedPercentage.today.inQueue` | v2+ | Percentage of interactions transferred by the agent, calculated relative to total interactions accepted (current day) | | `transfersReceived.int-15m.inQueue` | v1+ | Warm and blind transfers routed to agent for handling. All incoming transfers (last 15-minutes) | | `transfersReceived.int-30m.inQueue` | v1+ | Warm and blind transfers routed to agent for handling. All incoming transfers (last 30-minutes) | | `transfersReceived.today.inQueue` | v1+ | Warm and blind transfers routed to agent for handling. All incoming transfers (current day) | | `warmTransfers.int-15m.inQueue` | v1+ | Number of warm transfers performed by the agent. Transfer where agent spoke to recipient first (last 15-minutes) | | `warmTransfers.int-30m.inQueue` | v1+ | Number of warm transfers performed by the agent. Transfer where agent spoke to recipient first (last 30-minutes) | | `warmTransfers.today.inQueue` | v1+ | Number of warm transfers performed by the agent. Transfer where agent spoke to recipient first (current day) | | `workingOfflineTime.int-15m` | v1+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions (last 15-minutes) | | `workingOfflineTime.int-30m` | v1+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions (last 30-minutes) | | `workingOfflineTime.today` | v1+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions (current day) | | `workingOfflineTimePercentage.int-15m` | v1+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status (last 15-minutes) | | `workingOfflineTimePercentage.int-30m` | v1+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status (last 30-minutes) | | `workingOfflineTimePercentage.today` | v1+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status (current day) | | `wrapUpTime.int-15m` | v1+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer (last 15-minutes) | | `wrapUpTime.int-30m` | v1+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer (last 30-minutes) | | `wrapUpTime.today` | v1+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer (current day) | | `wrapUpTimePercentage.int-15m` | v1+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state (last 15-minutes) | | `wrapUpTimePercentage.int-30m` | v1+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state (last 30-minutes) | | `wrapUpTimePercentage.today` | v1+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state (current day) |
### 5.4. Agent Metrics Glossary - Group Context This glossary provides comprehensive definitions for all metrics available when querying agent statistics within a group context via the Real-time API. These metrics apply to the following endpoints: * `GET /realtime-metrics/groups/{group-id}/agents` - All agents in a group * `GET /realtime-metrics/groups/{group-id}/agents/{agent-id}` - Individual agent in a group
Click to expand Agent Metrics Glossary - Group Context (144 metrics) **Version** indicates minimum Real-time API version where metric became available on this API endpoint. | Metric | Version | Description | |----------------------------------------|---------|-------------| | `accepted.int-15m` | v1+ | Total interactions answered by the agent. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (last 15-minutes) | | `accepted.int-30m` | v1+ | Total interactions answered by the agent. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (last 30-minutes) | | `accepted.today` | v1+ | Total interactions answered by the agent. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (current day) | | `activeChannels.rt` | v1+ | List of communication channels with interactions where the agent is actively engaged (Offering, Handling, or Wrap Up state), showing channel name and count of such interactions in each channel (currently) | | `activeDirections.rt` | v1+ | List of interaction directions (Inbound, Outbound) with interactions where the agent is actively engaged (Offering, Handling, or Wrap Up state), showing direction and count of such interactions in each direction (currently) | | `activeInteractionsCount.rt` | v1+ | Total number of interactions where the agent is actively engaged (Offering, Handling, or Wrap Up state) across all queues, channels, and directions (currently) | | `activeQueues.rt` | v1+ | List of queues with interactions where the agent is actively engaged (Offering, Handling, or Wrap Up state), showing queue name and count of such interactions in each queue (currently) | | `alerting.rt` | v1+ | Number of interactions currently being presented to the agent, awaiting acceptance or rejection, across all of the agent's queues and including direct assignments. Excludes internal agent-to-agent calls and chats (currently) | | `availableTime.int-15m` | v1+ | Total time the agent spent in Available state, ready to receive incoming interactions (last 15-minutes) | | `availableTime.int-30m` | v1+ | Total time the agent spent in Available state, ready to receive incoming interactions (last 30-minutes) | | `availableTime.today` | v1+ | Total time the agent spent in Available state, ready to receive incoming interactions (current day) | | `availableTimePercentage.int-15m` | v1+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work (last 15-minutes) | | `availableTimePercentage.int-30m` | v1+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work (last 30-minutes) | | `availableTimePercentage.today` | v1+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work (current day) | | `averageHandlingTime.int-15m` | v1+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (last 15-minutes) | | `averageHandlingTime.int-30m` | v1+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (last 30-minutes) | | `averageHandlingTime.today` | v1+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (current day) | | `averageHoldTime.int-15m` | v2+ | Average time the agent placed customers on hold (last 15-minutes) | | `averageHoldTime.int-30m` | v2+ | Average time the agent placed customers on hold (last 30-minutes) | | `averageHoldTime.today` | v2+ | Average time the agent placed customers on hold (current day) | | `averageOfferingTime.int-15m` | v1+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (last 15-minutes) | | `averageOfferingTime.int-30m` | v1+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (last 30-minutes) | | `averageOfferingTime.today` | v1+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (current day) | | `averageWrapUpTime.int-15m` | v1+ | Average post-processing time. Time spent by agents completing administrative tasks after finishing handling an interaction (last 15-minutes) | | `averageWrapUpTime.int-30m` | v1+ | Average post-processing time. Time spent by agents completing administrative tasks after finishing handling an interaction (last 30-minutes) | | `averageWrapUpTime.today` | v1+ | Average post-processing time. Time spent by agents completing administrative tasks after finishing handling an interaction (current day) | | `blindTransfers.int-15m` | v1+ | Number of blind transfers performed by the agent. Transfer where agent does not speak to recipient first (last 15-minutes) | | `blindTransfers.int-30m` | v1+ | Number of blind transfers performed by the agent. Transfer where agent does not speak to recipient first (last 30-minutes) | | `blindTransfers.today` | v1+ | Number of blind transfers performed by the agent. Transfer where agent does not speak to recipient first (current day) | | `busyTime.int-15m` | v1+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities (last 15-minutes) | | `busyTime.int-30m` | v1+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities (last 30-minutes) | | `busyTime.today` | v1+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities (current day) | | `busyTimePercentage.int-15m` | v1+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working (last 15-minutes) | | `busyTimePercentage.int-30m` | v1+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working (last 30-minutes) | | `busyTimePercentage.today` | v1+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working (current day) | | `conferenceTime.int-15m` | v1+ | Total cumulative duration the agent spent in multi-party conference calls (last 15-minutes) | | `conferenceTime.int-30m` | v1+ | Total cumulative duration the agent spent in multi-party conference calls (last 30-minutes) | | `conferenceTime.today` | v1+ | Total cumulative duration the agent spent in multi-party conference calls (current day) | | `conferences.int-15m` | v1+ | Total number of conferences established by the agent (last 15-minutes) | | `conferences.int-30m` | v1+ | Total number of conferences established by the agent (last 30-minutes) | | `conferences.today` | v1+ | Total number of conferences established by the agent (current day) | | `consultations.int-15m` | v1+ | Times an agent successfully established an outbound call while another call is on hold (last 15-minutes) | | `consultations.int-30m` | v1+ | Times an agent successfully established an outbound call while another call is on hold (last 30-minutes) | | `consultations.today` | v1+ | Times an agent successfully established an outbound call while another call is on hold (current day) | | `directInboundTime.int-15m` | v1+ | Total cumulative duration the agent spent on direct inbound calls, excluding agent-to-agent calls (last 15-minutes) | | `directInboundTime.int-30m` | v1+ | Total cumulative duration the agent spent on direct inbound calls, excluding agent-to-agent calls (last 30-minutes) | | `directInboundTime.today` | v1+ | Total cumulative duration the agent spent on direct inbound calls, excluding agent-to-agent calls (current day) | | `directInbounds.int-15m` | v1+ | Total number of direct inbound calls to the agent excluding agent-to-agent calls (last 15-minutes) | | `directInbounds.int-30m` | v1+ | Total number of direct inbound calls to the agent excluding agent-to-agent calls (last 30-minutes) | | `directInbounds.today` | v1+ | Total number of direct inbound calls to the agent excluding agent-to-agent calls (current day) | | `directOutboundTime.int-15m` | v1+ | Total cumulative duration the agent spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls (last 15-minutes) | | `directOutboundTime.int-30m` | v1+ | Total cumulative duration the agent spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls (last 30-minutes) | | `directOutboundTime.today` | v1+ | Total cumulative duration the agent spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls (current day) | | `directOutbounds.int-15m` | v1+ | Number of calls made by the agent excluding outbound queue calls and agent-to-agent calls (last 15-minutes) | | `directOutbounds.int-30m` | v1+ | Number of calls made by the agent excluding outbound queue calls and agent-to-agent calls (last 30-minutes) | | `directOutbounds.today` | v1+ | Number of calls made by the agent excluding outbound queue calls and agent-to-agent calls (current day) | | `handlingTime.int-15m` | v1+ | Total time the agent spent in Handling state, actively processing interactions (last 15-minutes) | | `handlingTime.int-30m` | v1+ | Total time the agent spent in Handling state, actively processing interactions (last 30-minutes) | | `handlingTime.today` | v1+ | Total time the agent spent in Handling state, actively processing interactions (current day) | | `handlingTimePercentage.int-15m` | v1+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions (last 15-minutes) | | `handlingTimePercentage.int-30m` | v1+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions (last 30-minutes) | | `handlingTimePercentage.today` | v1+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions (current day) | | `hold.int-15m` | v1+ | Number of occasions the agent placed customers on hold (last 15-minutes) | | `hold.int-30m` | v1+ | Number of occasions the agent placed customers on hold (last 30-minutes) | | `hold.today` | v1+ | Number of occasions the agent placed customers on hold (current day) | | `internalCalls.int-15m` | v1+ | Total number of agent-to-agent calls initiated or received by the agent (last 15-minutes) | | `internalCalls.int-30m` | v1+ | Total number of agent-to-agent calls initiated or received by the agent (last 30-minutes) | | `internalCalls.today` | v1+ | Total number of agent-to-agent calls initiated or received by the agent (current day) | | `internalCallsInitiated.int-15m` | v1+ | Number of agent-to-agent calls initiated by this agent (last 15-minutes) | | `internalCallsInitiated.int-30m` | v1+ | Number of agent-to-agent calls initiated by this agent (last 30-minutes) | | `internalCallsInitiated.today` | v1+ | Number of agent-to-agent calls initiated by this agent (current day) | | `internalCallsReceived.int-15m` | v1+ | Number of agent-to-agent calls received by this agent (last 15-minutes) | | `internalCallsReceived.int-30m` | v1+ | Number of agent-to-agent calls received by this agent (last 30-minutes) | | `internalCallsReceived.today` | v1+ | Number of agent-to-agent calls received by this agent (current day) | | `internalCallsTime.int-15m` | v1+ | Total cumulative duration the agent spent on agent-to-agent calls, both initiated and received (last 15-minutes) | | `internalCallsTime.int-30m` | v1+ | Total cumulative duration the agent spent on agent-to-agent calls, both initiated and received (last 30-minutes) | | `internalCallsTime.today` | v1+ | Total cumulative duration the agent spent on agent-to-agent calls, both initiated and received (current day) | | `lastLogin.rt` | v1+ | Timestamp representing the agent's most recent login to the system (currently) | | `lastLogout.rt` | v1+ | Timestamp representing the agent's most recent logout from the system (currently) | | `lastStatusChange.rt` | v5+ | Timestamp when the agent's operational status last changed (currently) | | `lastStatusCodeChange.rt` | v5+ | Timestamp when the agent's status code last changed (currently) | | `line1Status.rt` | v1+ | Current operational status of the agent's first communication line (currently) | | `line1TimeOnStatus.rt` | v1+ | Time in milliseconds since the agent's first line status last changed (currently) | | `line2Status.rt` | v1+ | Current operational status of the agent's second communication line (currently) | | `line2TimeOnStatus.rt` | v1+ | Time in milliseconds since the agent's second line status last changed (currently) | | `loggedInTime.int-15m` | v1+ | Total time the agent maintained active system connection and was available for work across all queues and activities (last 15-minutes) | | `loggedInTime.int-30m` | v1+ | Total time the agent maintained active system connection and was available for work across all queues and activities (last 30-minutes) | | `loggedInTime.rt` | v1+ | Cumulative time the agent has maintained active system connection during their current login session (currently) | | `loggedInTime.today` | v1+ | Total time the agent maintained active system connection and was available for work across all queues and activities (current day) | | `longestHold.int-15m` | v1+ | Maximum single continuous hold duration when agent placed customer on hold (last 15-minutes) | | `longestHold.int-30m` | v1+ | Maximum single continuous hold duration when agent placed customer on hold (last 30-minutes) | | `longestHold.today` | v1+ | Maximum single continuous hold duration when agent placed customer on hold (current day) | | `longestOffering.int-15m` | v1+ | Maximum duration from when interaction was offered until agent accepted or rejected it. Shows longest time interaction remained in Offering state (last 15-minutes) | | `longestOffering.int-30m` | v1+ | Maximum duration from when interaction was offered until agent accepted or rejected it. Shows longest time interaction remained in Offering state (last 30-minutes) | | `longestOffering.today` | v1+ | Maximum duration from when interaction was offered until agent accepted or rejected it. Shows longest time interaction remained in Offering state (current day) | | `offered.int-15m` | v1+ | Total interactions presented to the agent for acceptance or rejection. Includes interactions continuing from prior intervals (last 15-minutes) | | `offered.int-30m` | v1+ | Total interactions presented to the agent for acceptance or rejection. Includes interactions continuing from prior intervals (last 30-minutes) | | `offered.today` | v1+ | Total interactions presented to the agent for acceptance or rejection. Includes interactions continuing from prior intervals (current day) | | `offeringTime.int-15m` | v1+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities (last 15-minutes) | | `offeringTime.int-30m` | v1+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities (last 30-minutes) | | `offeringTime.today` | v1+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities (current day) | | `onBreakTime.int-15m` | v1+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions (last 15-minutes) | | `onBreakTime.int-30m` | v1+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions (last 30-minutes) | | `onBreakTime.today` | v1+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions (current day) | | `onBreakTimePercentage.int-15m` | v1+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break (last 15-minutes) | | `onBreakTimePercentage.int-30m` | v1+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break (last 30-minutes) | | `onBreakTimePercentage.today` | v1+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break (current day) | | `onHoldTime.int-15m` | v1+ | Total duration the agent kept customers on hold. Sum of all hold periods (last 15-minutes) | | `onHoldTime.int-30m` | v1+ | Total duration the agent kept customers on hold. Sum of all hold periods (last 30-minutes) | | `onHoldTime.today` | v1+ | Total duration the agent kept customers on hold. Sum of all hold periods (current day) | | `rejectTimeout.int-15m` | v1+ | Count of interactions automatically rejected when agent did not respond within configured timeout period (last 15-minutes) | | `rejectTimeout.int-30m` | v1+ | Count of interactions automatically rejected when agent did not respond within configured timeout period (last 30-minutes) | | `rejectTimeout.today` | v1+ | Count of interactions automatically rejected when agent did not respond within configured timeout period (current day) | | `rejected.int-15m` | v1+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer (last 15-minutes) | | `rejected.int-30m` | v1+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer (last 30-minutes) | | `rejected.today` | v1+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer (current day) | | `status.rt` | v1+ | Agent's current operational state showing system status. Examples: Available, Handling, OnBreak, LoggedOut, WorkingOffline (currently) | | `statusCode.rt` | v1+ | Specific reason code that justifies or details the agent's current operational status (currently) | | `timeOnStatus.rt` | v1+ | Elapsed duration in milliseconds showing how long the agent has maintained their current operational status (currently) | | `timeOnStatusCode.rt` | v5+ | Elapsed duration in milliseconds showing how long the agent has maintained their current status code. The metric resets every time the status or the combination status + statusCode changes | | `transfersInitiated.int-15m` | v2+ | Warm and blind transfers initiated by the agent. All outgoing transfers (last 15-minutes) | | `transfersInitiated.int-30m` | v2+ | Warm and blind transfers initiated by the agent. All outgoing transfers (last 30-minutes) | | `transfersInitiated.today` | v2+ | Warm and blind transfers initiated by the agent. All outgoing transfers (current day) | | `transfersInitiatedPercentage.int-15m` | v2+ | Percentage of interactions transferred by the agent, calculated relative to total interactions accepted (last 15-minutes) | | `transfersInitiatedPercentage.int-30m` | v2+ | Percentage of interactions transferred by the agent, calculated relative to total interactions accepted (last 30-minutes) | | `transfersInitiatedPercentage.today` | v2+ | Percentage of interactions transferred by the agent, calculated relative to total interactions accepted (current day) | | `transfersReceived.int-15m` | v1+ | Warm and blind transfers routed to agent for handling. All incoming transfers (last 15-minutes) | | `transfersReceived.int-30m` | v1+ | Warm and blind transfers routed to agent for handling. All incoming transfers (last 30-minutes) | | `transfersReceived.today` | v1+ | Warm and blind transfers routed to agent for handling. All incoming transfers (current day) | | `warmTransfers.int-15m` | v1+ | Number of warm transfers performed by the agent. Transfer where agent spoke to recipient first (last 15-minutes) | | `warmTransfers.int-30m` | v1+ | Number of warm transfers performed by the agent. Transfer where agent spoke to recipient first (last 30-minutes) | | `warmTransfers.today` | v1+ | Number of warm transfers performed by the agent. Transfer where agent spoke to recipient first (current day) | | `workingOfflineTime.int-15m` | v1+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions (last 15-minutes) | | `workingOfflineTime.int-30m` | v1+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions (last 30-minutes) | | `workingOfflineTime.today` | v1+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions (current day) | | `workingOfflineTimePercentage.int-15m` | v1+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status (last 15-minutes) | | `workingOfflineTimePercentage.int-30m` | v1+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status (last 30-minutes) | | `workingOfflineTimePercentage.today` | v1+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status (current day) | | `wrapUpTime.int-15m` | v1+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer (last 15-minutes) | | `wrapUpTime.int-30m` | v1+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer (last 30-minutes) | | `wrapUpTime.today` | v1+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer (current day) | | `wrapUpTimePercentage.int-15m` | v1+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state (last 15-minutes) | | `wrapUpTimePercentage.int-30m` | v1+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state (last 30-minutes) | | `wrapUpTimePercentage.today` | v1+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state (current day) |
### 5.5. Agent Metrics Glossary - All Agents This glossary provides comprehensive definitions for all metrics available when querying agent statistics for all agents at once via the Real-time API. These metrics apply to the following endpoints: * `GET /realtime-metrics/agents` - All agents in the tenant * `GET /realtime-metrics/agents?agent-ids={agent-id}` - Specific agents in the tenant
Click to expand Agent Metrics Glossary - All Agents (144 metrics) **Version** indicates minimum Real-time API version where metric became available on this API endpoint. | Metric | Version | Description | |----------------------------------------|---------|-------------| | `accepted.int-15m` | v5+ | Total interactions answered by the agent. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (last 15-minutes) | | `accepted.int-30m` | v5+ | Total interactions answered by the agent. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (last 30-minutes) | | `accepted.today` | v5+ | Total interactions answered by the agent. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (current day) | | `activeChannels.rt` | v5+ | List of communication channels with interactions where the agent is actively engaged (Offering, Handling, or Wrap Up state), showing channel name and count of such interactions in each channel (currently) | | `activeDirections.rt` | v5+ | List of interaction directions (Inbound, Outbound) with interactions where the agent is actively engaged (Offering, Handling, or Wrap Up state), showing direction and count of such interactions in each direction (currently) | | `activeInteractionsCount.rt` | v5+ | Total number of interactions where the agent is actively engaged (Offering, Handling, or Wrap Up state) across all queues, channels, and directions (currently) | | `activeQueues.rt` | v5+ | List of queues with interactions where the agent is actively engaged (Offering, Handling, or Wrap Up state), showing queue name and count of such interactions in each queue (currently) | | `alerting.rt` | v5+ | Number of interactions currently being presented to the agent, awaiting acceptance or rejection, across all of the agent's queues and including direct assignments. Excludes internal agent-to-agent calls and chats (currently) | | `availableTime.int-15m` | v5+ | Total time the agent spent in Available state, ready to receive incoming interactions (last 15-minutes) | | `availableTime.int-30m` | v5+ | Total time the agent spent in Available state, ready to receive incoming interactions (last 30-minutes) | | `availableTime.today` | v5+ | Total time the agent spent in Available state, ready to receive incoming interactions (current day) | | `availableTimePercentage.int-15m` | v5+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work (last 15-minutes) | | `availableTimePercentage.int-30m` | v5+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work (last 30-minutes) | | `availableTimePercentage.today` | v5+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work (current day) | | `averageHandlingTime.int-15m` | v5+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (last 15-minutes) | | `averageHandlingTime.int-30m` | v5+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (last 30-minutes) | | `averageHandlingTime.today` | v5+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (current day) | | `averageHoldTime.int-15m` | v5+ | Average time the agent placed customers on hold (last 15-minutes) | | `averageHoldTime.int-30m` | v5+ | Average time the agent placed customers on hold (last 30-minutes) | | `averageHoldTime.today` | v5+ | Average time the agent placed customers on hold (current day) | | `averageOfferingTime.int-15m` | v5+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (last 15-minutes) | | `averageOfferingTime.int-30m` | v5+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (last 30-minutes) | | `averageOfferingTime.today` | v5+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (current day) | | `averageWrapUpTime.int-15m` | v5+ | Average post-processing time. Time spent by agents completing administrative tasks after finishing handling an interaction (last 15-minutes) | | `averageWrapUpTime.int-30m` | v5+ | Average post-processing time. Time spent by agents completing administrative tasks after finishing handling an interaction (last 30-minutes) | | `averageWrapUpTime.today` | v5+ | Average post-processing time. Time spent by agents completing administrative tasks after finishing handling an interaction (current day) | | `blindTransfers.int-15m` | v5+ | Number of blind transfers performed by the agent. Transfer where agent does not speak to recipient first (last 15-minutes) | | `blindTransfers.int-30m` | v5+ | Number of blind transfers performed by the agent. Transfer where agent does not speak to recipient first (last 30-minutes) | | `blindTransfers.today` | v5+ | Number of blind transfers performed by the agent. Transfer where agent does not speak to recipient first (current day) | | `busyTime.int-15m` | v5+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities (last 15-minutes) | | `busyTime.int-30m` | v5+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities (last 30-minutes) | | `busyTime.today` | v5+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities (current day) | | `busyTimePercentage.int-15m` | v5+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working (last 15-minutes) | | `busyTimePercentage.int-30m` | v5+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working (last 30-minutes) | | `busyTimePercentage.today` | v5+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working (current day) | | `conferenceTime.int-15m` | v5+ | Total cumulative duration the agent spent in multi-party conference calls (last 15-minutes) | | `conferenceTime.int-30m` | v5+ | Total cumulative duration the agent spent in multi-party conference calls (last 30-minutes) | | `conferenceTime.today` | v5+ | Total cumulative duration the agent spent in multi-party conference calls (current day) | | `conferences.int-15m` | v5+ | Total number of conferences established by the agent (last 15-minutes) | | `conferences.int-30m` | v5+ | Total number of conferences established by the agent (last 30-minutes) | | `conferences.today` | v5+ | Total number of conferences established by the agent (current day) | | `consultations.int-15m` | v5+ | Times an agent successfully established an outbound call while another call is on hold (last 15-minutes) | | `consultations.int-30m` | v5+ | Times an agent successfully established an outbound call while another call is on hold (last 30-minutes) | | `consultations.today` | v5+ | Times an agent successfully established an outbound call while another call is on hold (current day) | | `directInboundTime.int-15m` | v5+ | Total cumulative duration the agent spent on direct inbound calls, excluding agent-to-agent calls (last 15-minutes) | | `directInboundTime.int-30m` | v5+ | Total cumulative duration the agent spent on direct inbound calls, excluding agent-to-agent calls (last 30-minutes) | | `directInboundTime.today` | v5+ | Total cumulative duration the agent spent on direct inbound calls, excluding agent-to-agent calls (current day) | | `directInbounds.int-15m` | v5+ | Total number of direct inbound calls to the agent excluding agent-to-agent calls (last 15-minutes) | | `directInbounds.int-30m` | v5+ | Total number of direct inbound calls to the agent excluding agent-to-agent calls (last 30-minutes) | | `directInbounds.today` | v5+ | Total number of direct inbound calls to the agent excluding agent-to-agent calls (current day) | | `directOutboundTime.int-15m` | v5+ | Total cumulative duration the agent spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls (last 15-minutes) | | `directOutboundTime.int-30m` | v5+ | Total cumulative duration the agent spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls (last 30-minutes) | | `directOutboundTime.today` | v5+ | Total cumulative duration the agent spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls (current day) | | `directOutbounds.int-15m` | v5+ | Number of calls made by the agent excluding outbound queue calls and agent-to-agent calls (last 15-minutes) | | `directOutbounds.int-30m` | v5+ | Number of calls made by the agent excluding outbound queue calls and agent-to-agent calls (last 30-minutes) | | `directOutbounds.today` | v5+ | Number of calls made by the agent excluding outbound queue calls and agent-to-agent calls (current day) | | `handlingTime.int-15m` | v5+ | Total time the agent spent in Handling state, actively processing interactions (last 15-minutes) | | `handlingTime.int-30m` | v5+ | Total time the agent spent in Handling state, actively processing interactions (last 30-minutes) | | `handlingTime.today` | v5+ | Total time the agent spent in Handling state, actively processing interactions (current day) | | `handlingTimePercentage.int-15m` | v5+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions (last 15-minutes) | | `handlingTimePercentage.int-30m` | v5+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions (last 30-minutes) | | `handlingTimePercentage.today` | v5+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions (current day) | | `hold.int-15m` | v5+ | Number of occasions the agent placed customers on hold (last 15-minutes) | | `hold.int-30m` | v5+ | Number of occasions the agent placed customers on hold (last 30-minutes) | | `hold.today` | v5+ | Number of occasions the agent placed customers on hold (current day) | | `internalCalls.int-15m` | v5+ | Total number of agent-to-agent calls initiated or received by the agent (last 15-minutes) | | `internalCalls.int-30m` | v5+ | Total number of agent-to-agent calls initiated or received by the agent (last 30-minutes) | | `internalCalls.today` | v5+ | Total number of agent-to-agent calls initiated or received by the agent (current day) | | `internalCallsInitiated.int-15m` | v5+ | Number of agent-to-agent calls initiated by this agent (last 15-minutes) | | `internalCallsInitiated.int-30m` | v5+ | Number of agent-to-agent calls initiated by this agent (last 30-minutes) | | `internalCallsInitiated.today` | v5+ | Number of agent-to-agent calls initiated by this agent (current day) | | `internalCallsReceived.int-15m` | v5+ | Number of agent-to-agent calls received by this agent (last 15-minutes) | | `internalCallsReceived.int-30m` | v5+ | Number of agent-to-agent calls received by this agent (last 30-minutes) | | `internalCallsReceived.today` | v5+ | Number of agent-to-agent calls received by this agent (current day) | | `internalCallsTime.int-15m` | v5+ | Total cumulative duration the agent spent on agent-to-agent calls, both initiated and received (last 15-minutes) | | `internalCallsTime.int-30m` | v5+ | Total cumulative duration the agent spent on agent-to-agent calls, both initiated and received (last 30-minutes) | | `internalCallsTime.today` | v5+ | Total cumulative duration the agent spent on agent-to-agent calls, both initiated and received (current day) | | `lastLogin.rt` | v5+ | Timestamp representing the agent's most recent login to the system (currently) | | `lastLogout.rt` | v5+ | Timestamp representing the agent's most recent logout from the system (currently) | | `lastStatusChange.rt` | v5+ | Timestamp when the agent's operational status last changed (currently) | | `lastStatusCodeChange.rt` | v5+ | Timestamp when the agent's status code last changed (currently) | | `line1Status.rt` | v5+ | Current operational status of the agent's first communication line (currently) | | `line1TimeOnStatus.rt` | v5+ | Time in milliseconds since the agent's first line status last changed (currently) | | `line2Status.rt` | v5+ | Current operational status of the agent's second communication line (currently) | | `line2TimeOnStatus.rt` | v5+ | Time in milliseconds since the agent's second line status last changed (currently) | | `loggedInTime.int-15m` | v5+ | Total time the agent maintained active system connection and was available for work across all queues and activities (last 15-minutes) | | `loggedInTime.int-30m` | v5+ | Total time the agent maintained active system connection and was available for work across all queues and activities (last 30-minutes) | | `loggedInTime.rt` | v5+ | Cumulative time the agent has maintained active system connection during their current login session (currently) | | `loggedInTime.today` | v5+ | Total time the agent maintained active system connection and was available for work across all queues and activities (current day) | | `longestHold.int-15m` | v5+ | Maximum single continuous hold duration when agent placed customer on hold (last 15-minutes) | | `longestHold.int-30m` | v5+ | Maximum single continuous hold duration when agent placed customer on hold (last 30-minutes) | | `longestHold.today` | v5+ | Maximum single continuous hold duration when agent placed customer on hold (current day) | | `longestOffering.int-15m` | v5+ | Maximum duration from when interaction was offered until agent accepted or rejected it. Shows longest time interaction remained in Offering state (last 15-minutes) | | `longestOffering.int-30m` | v5+ | Maximum duration from when interaction was offered until agent accepted or rejected it. Shows longest time interaction remained in Offering state (last 30-minutes) | | `longestOffering.today` | v5+ | Maximum duration from when interaction was offered until agent accepted or rejected it. Shows longest time interaction remained in Offering state (current day) | | `offered.int-15m` | v5+ | Total interactions presented to the agent for acceptance or rejection. Includes interactions continuing from prior intervals (last 15-minutes) | | `offered.int-30m` | v5+ | Total interactions presented to the agent for acceptance or rejection. Includes interactions continuing from prior intervals (last 30-minutes) | | `offered.today` | v5+ | Total interactions presented to the agent for acceptance or rejection. Includes interactions continuing from prior intervals (current day) | | `offeringTime.int-15m` | v5+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities (last 15-minutes) | | `offeringTime.int-30m` | v5+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities (last 30-minutes) | | `offeringTime.today` | v5+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities (current day) | | `onBreakTime.int-15m` | v5+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions (last 15-minutes) | | `onBreakTime.int-30m` | v5+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions (last 30-minutes) | | `onBreakTime.today` | v5+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions (current day) | | `onBreakTimePercentage.int-15m` | v5+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break (last 15-minutes) | | `onBreakTimePercentage.int-30m` | v5+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break (last 30-minutes) | | `onBreakTimePercentage.today` | v5+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break (current day) | | `onHoldTime.int-15m` | v5+ | Total duration the agent kept customers on hold. Sum of all hold periods (last 15-minutes) | | `onHoldTime.int-30m` | v5+ | Total duration the agent kept customers on hold. Sum of all hold periods (last 30-minutes) | | `onHoldTime.today` | v5+ | Total duration the agent kept customers on hold. Sum of all hold periods (current day) | | `rejectTimeout.int-15m` | v5+ | Count of interactions automatically rejected when agent did not respond within configured timeout period (last 15-minutes) | | `rejectTimeout.int-30m` | v5+ | Count of interactions automatically rejected when agent did not respond within configured timeout period (last 30-minutes) | | `rejectTimeout.today` | v5+ | Count of interactions automatically rejected when agent did not respond within configured timeout period (current day) | | `rejected.int-15m` | v5+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer (last 15-minutes) | | `rejected.int-30m` | v5+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer (last 30-minutes) | | `rejected.today` | v5+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer (current day) | | `status.rt` | v5+ | Agent's current operational state showing system status. Examples: Available, Handling, OnBreak, LoggedOut, WorkingOffline (currently) | | `statusCode.rt` | v5+ | Specific reason code that justifies or details the agent's current operational status (currently) | | `timeOnStatus.rt` | v5+ | Elapsed duration in milliseconds showing how long the agent has maintained their current operational status (currently) | | `timeOnStatusCode.rt` | v5+ | Elapsed duration in milliseconds showing how long the agent has maintained their current status code. The metric resets every time the status or the combination status + statusCode changes | | `transfersInitiated.int-15m` | v5+ | Warm and blind transfers initiated by the agent. All outgoing transfers (last 15-minutes) | | `transfersInitiated.int-30m` | v5+ | Warm and blind transfers initiated by the agent. All outgoing transfers (last 30-minutes) | | `transfersInitiated.today` | v5+ | Warm and blind transfers initiated by the agent. All outgoing transfers (current day) | | `transfersInitiatedPercentage.int-15m` | v5+ | Percentage of interactions transferred by the agent, calculated relative to total interactions accepted (last 15-minutes) | | `transfersInitiatedPercentage.int-30m` | v5+ | Percentage of interactions transferred by the agent, calculated relative to total interactions accepted (last 30-minutes) | | `transfersInitiatedPercentage.today` | v5+ | Percentage of interactions transferred by the agent, calculated relative to total interactions accepted (current day) | | `transfersReceived.int-15m` | v5+ | Warm and blind transfers routed to agent for handling. All incoming transfers (last 15-minutes) | | `transfersReceived.int-30m` | v5+ | Warm and blind transfers routed to agent for handling. All incoming transfers (last 30-minutes) | | `transfersReceived.today` | v5+ | Warm and blind transfers routed to agent for handling. All incoming transfers (current day) | | `warmTransfers.int-15m` | v5+ | Number of warm transfers performed by the agent. Transfer where agent spoke to recipient first (last 15-minutes) | | `warmTransfers.int-30m` | v5+ | Number of warm transfers performed by the agent. Transfer where agent spoke to recipient first (last 30-minutes) | | `warmTransfers.today` | v5+ | Number of warm transfers performed by the agent. Transfer where agent spoke to recipient first (current day) | | `workingOfflineTime.int-15m` | v5+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions (last 15-minutes) | | `workingOfflineTime.int-30m` | v5+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions (last 30-minutes) | | `workingOfflineTime.today` | v5+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions (current day) | | `workingOfflineTimePercentage.int-15m` | v5+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status (last 15-minutes) | | `workingOfflineTimePercentage.int-30m` | v5+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status (last 30-minutes) | | `workingOfflineTimePercentage.today` | v5+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status (current day) | | `wrapUpTime.int-15m` | v5+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer (last 15-minutes) | | `wrapUpTime.int-30m` | v5+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer (last 30-minutes) | | `wrapUpTime.today` | v5+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer (current day) | | `wrapUpTimePercentage.int-15m` | v5+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state (last 15-minutes) | | `wrapUpTimePercentage.int-30m` | v5+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state (last 30-minutes) | | `wrapUpTimePercentage.today` | v5+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state (current day) |
### 5.6. Agent Metrics Glossary - Multiple Queues Context This glossary provides comprehensive definitions for all metrics available when querying agent statistics across multiple queues via the Real-time API. These metrics apply to the following endpoint: * `GET /realtime-metrics/agents-in-queue-groups` - All agents across queues with optional filtering by specific queue IDs **Note on `.inQueue` metrics:** The `.inQueue` suffix indicates that the metric measures agent activity specific to interactions routed through the **current queue**. These metrics exclude agent activity from direct inbound/outbound calls, internal agent-to-agent calls, and other non-queue interactions. Use `.inQueue` metrics to analyze agent performance for the selected queue.
Click to expand Agent Metrics Glossary - Multiple Queues Context (140 metrics) **Version** indicates minimum Real-time API version where metric became available on this API endpoint. | Metric | Version | Description | |------------------------------------------------|---------|-------------| | `accepted.int-15m.inQueue` | v5+ | Total interactions answered by the agent. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (last 15-minutes) | | `accepted.int-30m.inQueue` | v5+ | Total interactions answered by the agent. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (last 30-minutes) | | `accepted.today.inQueue` | v5+ | Total interactions answered by the agent. Represents every call, chat, email or other interaction that was successfully connected to and handled by an agent (current day) | | `alerting.rt` | v5+ | Number of interactions currently being presented to the agent, awaiting acceptance or rejection, across all of the agent's queues — including queues not named in the request — and including direct assignments. Excludes internal agent-to-agent calls and chats (currently) | | `availableTime.int-15m` | v5+ | Total time the agent spent in Available state, ready to receive incoming interactions (last 15-minutes) | | `availableTime.int-30m` | v5+ | Total time the agent spent in Available state, ready to receive incoming interactions (last 30-minutes) | | `availableTime.today` | v5+ | Total time the agent spent in Available state, ready to receive incoming interactions (current day) | | `availableTimePercentage.int-15m` | v5+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work (last 15-minutes) | | `availableTimePercentage.int-30m` | v5+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work (last 30-minutes) | | `availableTimePercentage.today` | v5+ | Percentage of available time relative to total logged-in time. Shows the proportion of total login time the agent spent in Available state ready to receive work (current day) | | `averageHandlingTime.int-15m.inQueue` | v5+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (last 15-minutes) | | `averageHandlingTime.int-30m.inQueue` | v5+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (last 30-minutes) | | `averageHandlingTime.today.inQueue` | v5+ | Average time agents spend handling interactions including hold periods. Measured from when an agent accepts an interaction until they finish processing it, including any time the customer was placed on hold (current day) | | `averageHoldTime.int-15m.inQueue` | v5+ | Average time the agent placed customers on hold (last 15-minutes) | | `averageHoldTime.int-30m.inQueue` | v5+ | Average time the agent placed customers on hold (last 30-minutes) | | `averageHoldTime.today.inQueue` | v5+ | Average time the agent placed customers on hold (current day) | | `averageOfferingTime.int-15m.inQueue` | v5+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (last 15-minutes) | | `averageOfferingTime.int-30m.inQueue` | v5+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (last 30-minutes) | | `averageOfferingTime.today.inQueue` | v5+ | Average duration from interaction presentation to acceptance or rejection. Measures how long an interaction is offered to an agent before they either accept it or decline it (current day) | | `averageWrapUpTime.int-15m.inQueue` | v5+ | Average post-processing time. Time spent by agents completing administrative tasks after finishing handling an interaction (last 15-minutes) | | `averageWrapUpTime.int-30m.inQueue` | v5+ | Average post-processing time. Time spent by agents completing administrative tasks after finishing handling an interaction (last 30-minutes) | | `averageWrapUpTime.today.inQueue` | v5+ | Average post-processing time. Time spent by agents completing administrative tasks after finishing handling an interaction (current day) | | `blindTransfers.int-15m.inQueue` | v5+ | Number of blind transfers performed by the agent. Transfer where agent does not speak to recipient first (last 15-minutes) | | `blindTransfers.int-30m.inQueue` | v5+ | Number of blind transfers performed by the agent. Transfer where agent does not speak to recipient first (last 30-minutes) | | `blindTransfers.today.inQueue` | v5+ | Number of blind transfers performed by the agent. Transfer where agent does not speak to recipient first (current day) | | `busyTime.int-15m` | v5+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities (last 15-minutes) | | `busyTime.int-30m` | v5+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities (last 30-minutes) | | `busyTime.today` | v5+ | Combined duration the agent spent in Offering, Handling, and Wrap Up states across all activities. Time agent is actively engaged in work activities (current day) | | `busyTimePercentage.int-15m` | v5+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working (last 15-minutes) | | `busyTimePercentage.int-30m` | v5+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working (last 30-minutes) | | `busyTimePercentage.today` | v5+ | Percentage of busy time relative to total logged-in time. Shows proportion of time agent was actively working (current day) | | `conferenceTime.int-15m.inQueue` | v5+ | Total cumulative duration the agent spent in multi-party conference calls (last 15-minutes) | | `conferenceTime.int-30m.inQueue` | v5+ | Total cumulative duration the agent spent in multi-party conference calls (last 30-minutes) | | `conferenceTime.today.inQueue` | v5+ | Total cumulative duration the agent spent in multi-party conference calls (current day) | | `conferences.int-15m.inQueue` | v5+ | Total number of conferences established by the agent (last 15-minutes) | | `conferences.int-30m.inQueue` | v5+ | Total number of conferences established by the agent (last 30-minutes) | | `conferences.today.inQueue` | v5+ | Total number of conferences established by the agent (current day) | | `consultations.int-15m.inQueue` | v5+ | Times an agent successfully established an outbound call while another call is on hold (last 15-minutes) | | `consultations.int-30m.inQueue` | v5+ | Times an agent successfully established an outbound call while another call is on hold (last 30-minutes) | | `consultations.today.inQueue` | v5+ | Times an agent successfully established an outbound call while another call is on hold (current day) | | `directInboundTime.int-15m` | v5+ | Total cumulative duration the agent spent on direct inbound calls, excluding agent-to-agent calls (last 15-minutes) | | `directInboundTime.int-30m` | v5+ | Total cumulative duration the agent spent on direct inbound calls, excluding agent-to-agent calls (last 30-minutes) | | `directInboundTime.today` | v5+ | Total cumulative duration the agent spent on direct inbound calls, excluding agent-to-agent calls (current day) | | `directInbounds.int-15m` | v5+ | Total number of direct inbound calls to the agent excluding agent-to-agent calls (last 15-minutes) | | `directInbounds.int-30m` | v5+ | Total number of direct inbound calls to the agent excluding agent-to-agent calls (last 30-minutes) | | `directInbounds.today` | v5+ | Total number of direct inbound calls to the agent excluding agent-to-agent calls (current day) | | `directOutboundTime.int-15m` | v5+ | Total cumulative duration the agent spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls (last 15-minutes) | | `directOutboundTime.int-30m` | v5+ | Total cumulative duration the agent spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls (last 30-minutes) | | `directOutboundTime.today` | v5+ | Total cumulative duration the agent spent on direct outbound calls, excluding outbound queue calls and agent-to-agent calls (current day) | | `directOutbounds.int-15m` | v5+ | Number of calls made by the agent excluding outbound queue calls and agent-to-agent calls (last 15-minutes) | | `directOutbounds.int-30m` | v5+ | Number of calls made by the agent excluding outbound queue calls and agent-to-agent calls (last 30-minutes) | | `directOutbounds.today` | v5+ | Number of calls made by the agent excluding outbound queue calls and agent-to-agent calls (current day) | | `handlingTime.int-15m` | v5+ | Total time the agent spent in Handling state, actively processing interactions (last 15-minutes) | | `handlingTime.int-30m` | v5+ | Total time the agent spent in Handling state, actively processing interactions (last 30-minutes) | | `handlingTime.today` | v5+ | Total time the agent spent in Handling state, actively processing interactions (current day) | | `handlingTimePercentage.int-15m` | v5+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions (last 15-minutes) | | `handlingTimePercentage.int-30m` | v5+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions (last 30-minutes) | | `handlingTimePercentage.today` | v5+ | Percentage of handling time relative to total logged-in time. Shows what proportion of total login duration agent spent actively handling interactions (current day) | | `hold.int-15m.inQueue` | v5+ | Number of occasions the agent placed customers on hold (last 15-minutes) | | `hold.int-30m.inQueue` | v5+ | Number of occasions the agent placed customers on hold (last 30-minutes) | | `hold.today.inQueue` | v5+ | Number of occasions the agent placed customers on hold (current day) | | `internalCalls.int-15m` | v5+ | Total number of agent-to-agent calls initiated or received by the agent (last 15-minutes) | | `internalCalls.int-30m` | v5+ | Total number of agent-to-agent calls initiated or received by the agent (last 30-minutes) | | `internalCalls.today` | v5+ | Total number of agent-to-agent calls initiated or received by the agent (current day) | | `internalCallsInitiated.int-15m` | v5+ | Number of agent-to-agent calls initiated by this agent (last 15-minutes) | | `internalCallsInitiated.int-30m` | v5+ | Number of agent-to-agent calls initiated by this agent (last 30-minutes) | | `internalCallsInitiated.today` | v5+ | Number of agent-to-agent calls initiated by this agent (current day) | | `internalCallsReceived.int-15m` | v5+ | Number of agent-to-agent calls received by this agent (last 15-minutes) | | `internalCallsReceived.int-30m` | v5+ | Number of agent-to-agent calls received by this agent (last 30-minutes) | | `internalCallsReceived.today` | v5+ | Number of agent-to-agent calls received by this agent (current day) | | `internalCallsTime.int-15m` | v5+ | Total cumulative duration the agent spent on agent-to-agent calls, both initiated and received (last 15-minutes) | | `internalCallsTime.int-30m` | v5+ | Total cumulative duration the agent spent on agent-to-agent calls, both initiated and received (last 30-minutes) | | `internalCallsTime.today` | v5+ | Total cumulative duration the agent spent on agent-to-agent calls, both initiated and received (current day) | | `lastLogin.rt` | v5+ | Timestamp representing the agent's most recent login to the system (currently) | | `lastLogout.rt` | v5+ | Timestamp representing the agent's most recent logout from the system (currently) | | `lastStatusChange.rt` | v5+ | Timestamp when the agent's operational status last changed (currently) | | `lastStatusCodeChange.rt` | v5+ | Timestamp when the agent's status code last changed (currently) | | `line1Status.rt` | v5+ | Current operational status of the agent's first communication line (currently) | | `line1TimeOnStatus.rt` | v5+ | Time in milliseconds since the agent's first line status last changed (currently) | | `line2Status.rt` | v5+ | Current operational status of the agent's second communication line (currently) | | `line2TimeOnStatus.rt` | v5+ | Time in milliseconds since the agent's second line status last changed (currently) | | `loggedInTime.int-15m` | v5+ | Total time the agent maintained active system connection and was available for work across all queues and activities (last 15-minutes) | | `loggedInTime.int-30m` | v5+ | Total time the agent maintained active system connection and was available for work across all queues and activities (last 30-minutes) | | `loggedInTime.rt` | v5+ | Cumulative time the agent has maintained active system connection during their current login session (currently) | | `loggedInTime.today` | v5+ | Total time the agent maintained active system connection and was available for work across all queues and activities (current day) | | `longestHold.int-15m.inQueue` | v5+ | Maximum single continuous hold duration when agent placed customer on hold (last 15-minutes) | | `longestHold.int-30m.inQueue` | v5+ | Maximum single continuous hold duration when agent placed customer on hold (last 30-minutes) | | `longestHold.today.inQueue` | v5+ | Maximum single continuous hold duration when agent placed customer on hold (current day) | | `longestOffering.int-15m.inQueue` | v5+ | Maximum duration from when interaction was offered until agent accepted or rejected it. Shows longest time interaction remained in Offering state (last 15-minutes) | | `longestOffering.int-30m.inQueue` | v5+ | Maximum duration from when interaction was offered until agent accepted or rejected it. Shows longest time interaction remained in Offering state (last 30-minutes) | | `longestOffering.today.inQueue` | v5+ | Maximum duration from when interaction was offered until agent accepted or rejected it. Shows longest time interaction remained in Offering state (current day) | | `offered.int-15m.inQueue` | v5+ | Total interactions presented to the agent for acceptance or rejection. Includes interactions continuing from prior intervals (last 15-minutes) | | `offered.int-30m.inQueue` | v5+ | Total interactions presented to the agent for acceptance or rejection. Includes interactions continuing from prior intervals (last 30-minutes) | | `offered.today.inQueue` | v5+ | Total interactions presented to the agent for acceptance or rejection. Includes interactions continuing from prior intervals (current day) | | `offeringTime.int-15m` | v5+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities (last 15-minutes) | | `offeringTime.int-30m` | v5+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities (last 30-minutes) | | `offeringTime.today` | v5+ | Total duration the agent spent in Offering state waiting to accept or reject interactions across all activities (current day) | | `onBreakTime.int-15m` | v5+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions (last 15-minutes) | | `onBreakTime.int-30m` | v5+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions (last 30-minutes) | | `onBreakTime.today` | v5+ | Total duration the agent spent in On Break status, temporarily unavailable to receive new interactions (current day) | | `onBreakTimePercentage.int-15m` | v5+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break (last 15-minutes) | | `onBreakTimePercentage.int-30m` | v5+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break (last 30-minutes) | | `onBreakTimePercentage.today` | v5+ | Percentage of break time relative to total logged-in time. Shows what proportion of login duration agent spent on break (current day) | | `onHoldTime.int-15m.inQueue` | v5+ | Total duration the agent kept customers on hold. Sum of all hold periods (last 15-minutes) | | `onHoldTime.int-30m.inQueue` | v5+ | Total duration the agent kept customers on hold. Sum of all hold periods (last 30-minutes) | | `onHoldTime.today.inQueue` | v5+ | Total duration the agent kept customers on hold. Sum of all hold periods (current day) | | `rejectTimeout.int-15m.inQueue` | v5+ | Count of interactions automatically rejected when agent did not respond within configured timeout period (last 15-minutes) | | `rejectTimeout.int-30m.inQueue` | v5+ | Count of interactions automatically rejected when agent did not respond within configured timeout period (last 30-minutes) | | `rejectTimeout.today.inQueue` | v5+ | Count of interactions automatically rejected when agent did not respond within configured timeout period (current day) | | `rejected.int-15m.inQueue` | v5+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer (last 15-minutes) | | `rejected.int-30m.inQueue` | v5+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer (last 30-minutes) | | `rejected.today.inQueue` | v5+ | Count of interactions manually declined by agent when interaction was offered. Agent explicitly rejected the offer (current day) | | `status.rt` | v5+ | Agent's current operational state showing system status. Examples: Available, Handling, OnBreak, LoggedOut, WorkingOffline (currently) | | `statusCode.rt` | v5+ | Specific reason code that justifies or details the agent's current operational status (currently) | | `timeOnStatus.rt` | v5+ | Elapsed duration in milliseconds showing how long the agent has maintained their current operational status (currently) | | `timeOnStatusCode.rt` | v5+ | Elapsed duration in milliseconds showing how long the agent has maintained their current status code. The metric resets every time the status or the combination status + statusCode changes | | `transfersInitiated.int-15m.inQueue` | v5+ | Warm and blind transfers initiated by the agent. All outgoing transfers (last 15-minutes) | | `transfersInitiated.int-30m.inQueue` | v5+ | Warm and blind transfers initiated by the agent. All outgoing transfers (last 30-minutes) | | `transfersInitiated.today.inQueue` | v5+ | Warm and blind transfers initiated by the agent. All outgoing transfers (current day) | | `transfersInitiatedPercentage.int-15m.inQueue` | v5+ | Percentage of interactions transferred by the agent, calculated relative to total interactions accepted (last 15-minutes) | | `transfersInitiatedPercentage.int-30m.inQueue` | v5+ | Percentage of interactions transferred by the agent, calculated relative to total interactions accepted (last 30-minutes) | | `transfersInitiatedPercentage.today.inQueue` | v5+ | Percentage of interactions transferred by the agent, calculated relative to total interactions accepted (current day) | | `transfersReceived.int-15m.inQueue` | v5+ | Warm and blind transfers routed to agent for handling. All incoming transfers (last 15-minutes) | | `transfersReceived.int-30m.inQueue` | v5+ | Warm and blind transfers routed to agent for handling. All incoming transfers (last 30-minutes) | | `transfersReceived.today.inQueue` | v5+ | Warm and blind transfers routed to agent for handling. All incoming transfers (current day) | | `warmTransfers.int-15m.inQueue` | v5+ | Number of warm transfers performed by the agent. Transfer where agent spoke to recipient first (last 15-minutes) | | `warmTransfers.int-30m.inQueue` | v5+ | Number of warm transfers performed by the agent. Transfer where agent spoke to recipient first (last 30-minutes) | | `warmTransfers.today.inQueue` | v5+ | Number of warm transfers performed by the agent. Transfer where agent spoke to recipient first (current day) | | `workingOfflineTime.int-15m` | v5+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions (last 15-minutes) | | `workingOfflineTime.int-30m` | v5+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions (last 30-minutes) | | `workingOfflineTime.today` | v5+ | Total duration the agent spent in Working Offline status performing non-interactive work. Agent not available to receive new interactions (current day) | | `workingOfflineTimePercentage.int-15m` | v5+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status (last 15-minutes) | | `workingOfflineTimePercentage.int-30m` | v5+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status (last 30-minutes) | | `workingOfflineTimePercentage.today` | v5+ | Percentage of offline work time relative to total logged-in time. Shows what proportion of login duration agent spent in Working Offline status (current day) | | `wrapUpTime.int-15m` | v5+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer (last 15-minutes) | | `wrapUpTime.int-30m` | v5+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer (last 30-minutes) | | `wrapUpTime.today` | v5+ | Total duration the agent spent in Wrap Up state completing post-interaction administrative tasks across all activities after disconnecting from customer (current day) | | `wrapUpTimePercentage.int-15m` | v5+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state (last 15-minutes) | | `wrapUpTimePercentage.int-30m` | v5+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state (last 30-minutes) | | `wrapUpTimePercentage.today` | v5+ | Percentage of wrap-up time relative to total logged-in time. Shows what proportion of login duration agent spent finalizing interactions in Wrap Up state (current day) |
--- ## Cloud Storage Service Bulk Download Customers looking to download content in bulk from [Cloud Storage Service](/analytics/reference/searchobject) can follow the this multi step process. Use cases include downloading 8x8 Work Call Recordings, Meeting Recordings, Contact Center Recordings or any of the other data types in the Cloud Storage Service. > 📘 **You will need a working API key to begin** > > [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > To create a "Call Recording & Storage" API Keys, the user must first have the 'Cloud Storage API' assignment. This assignment must be granted by the Super Admin. > > For interacting with Cloud Storage Service `https://api.8x8.com/storage/{region}/v{version}/` * {region} to be replaced by a valid region based on region discovery * {version} to be replaced by current Version. Currently 3 resulting in /v3/ ## 1. Authenticate to retrieve access token [OAuth Authentication for 8x8 XCaaS APIs](/analytics/docs/oauth-authentication-for-8x8-xcaas-apis) is used to get a temporary `access_token` for use in with this API **Outputs For Next Step:** * access_token * expires_in The following steps will use the access_token as a Bearer Token form of authentication. This takes the form of the `Authorization` header being set to `Bearer access_token` (Space between Bearer and the access_token) ## 2. Get My Regions 8x8 Cloud Storage Service persists data regionally based on the customer locations/setup. For many customers this may be a single region for others this can be a number of regions. Each Region contains it's metadata/database and storage only. You cannot search in "us-east" and find items in "uk" since this could involve data export. ### Parameters **Method: GET** #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | region | ✓ | Pass any valid region in for the discovery process. `us-east` or `uk` (The region does not need to be one of your regions for this request | `us-east` | | version | ✓ | The current version is `v3` | v3 | ### My Regions Request genericus-eastuk ```bash curl --location --request GET 'https://api.8x8.com/storage/{region}/v3/regions' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer {access_token}' ``` ```bash curl --location --request GET 'https://api.8x8.com/storage/us-east/v3/regions' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer {access_token}' ``` ```bash curl --location --request GET 'https://api.8x8.com/storage/uk/v3/regions' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer {access_token}' ``` ### Regions Response ```json [ "us-east", "uk" ] ``` **Outputs For Next Step:** * region > 📘 **Only one region at a time can be searched.** > > If you have multiple regions and your use case spans regions. If you want to download all recordings for a specific date then steps 3 - 5 need to be performed per region > > ## 3. Find Objects > 🚧 **Only objects with a state of AVAILABLE are returned unless other object states are specifically requested.** > > To return objects in multiple states specify this in the filter. > > Example to return all objects that are REVOKED and AVAILABLE the filter should include `(objectState==AVAILABLE,objectState==REVOKED)` > > In FIQL ; = AND and , = OR > > ### Parameters **Method: GET** #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | region | ✓ | The region you are want to search in. **Must be one of the regions returned by the Regions request** | us-east | | version | ✓ | The current version is `v3` | v3 | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | pageKey | ☐ | Optional on first page, first page is 0 (zero), see pagination example below. | 0 | | limit | ☐ | Number of records per page, 100 is the default. | 200 | | filter | ☐ | filter to apply when searching See **FIQL Query Guide** | type==callcenterrecording;duration=gt=100 | | sortField | ☐ | the field to sort on | createdTime | | sortDirection | ☐ | whether to sort ascending `ASC` or descending `DESC` | ASC | In our example we are going to find all Contact Center Call Recordings in the `us-east` region `type==callcenterrecording` that have a duration of over 100 seconds `duration=gt=100` and we will sort be createdTime `sortField=createdTime` ascending `sortDirection=ASC` and get the first page `pageKey=0` and get 100 records per page `limit=100` Combined the query becomes `filter=type==callcenterrecording;duration=gt=100&sortField=createdTime&sortDirection=ASC&pageKey=0&limit=100` [Cloud Storage Service Objects](/analytics/docs/cloud-storage-service-objects) has a more examples of filtering options. ### Find Objects Request ```bash curl --location --request GET 'https://api.8x8.com/storage/{region}/v3/objects?filter=type==callcenterrecording;duration=gt=100&sortField=createdTime&sortDirection=ASC&pageKey=0&limit=100' \ --header 'Accept: application/json' \ --header 'Authorization: Bearer {access_token}' ``` ### Find Objects Response > 🚧 **TAGS SHORTENED TO KEEP SAMPLE SHORT** > > NOTE TAGS HAVE BEEN SHORTENED FOR SIMPLICITY, SEE [Cloud Storage Service Objects](/analytics/docs/cloud-storage-service-objects) for details on additional object types. > > **Response:** ```json { "lastPage": true, "pageKey": 100, "pageSize": 2, "content": [ { "id": "58bc5748-339b-43c2-ad15-572a35dc3aad", "type": "callcenterrecording", "mimeType": "audio/mpeg", "objectName": "int-1819420cbe2-sHtEBArUkmmtmyczfbcDGWMwl-phone-00-supertenantcsm01.mp3", "checksumType": "MD5", "checksum": "922ce8066731381f4a843e0bcf013826", "customerId": "0012J00002KTQJYQA5", "userId": "TMZIdbKzQGWvvi_04IC9Lg", "storedBytes": 3644352, "createdTime": "2022-06-24T05:22:35", "updatedTime": "2022-06-24T05:22:37", "objectState": "AVAILABLE", "bucketId": "4339ea5d-36c2-4105-8225-1f8e916ebebe", "tags": [ { "key": "callId", "value": "int-1819420cbe2-sHtEBArUkmmtmyczfbcDGWMwl-phone-00-supertenantcsm01" } ], "shared": false }, { "id": "b6aca571-ebe2-40ed-b553-24b8dc3bc035", "type": "callcenterrecording", "mimeType": "audio/mpeg", "objectName": "int-181940b61ea-IMzQUGTvUjs03b07NaxsLqfJ3-phone-00-supertenantcsm01.mp3", "checksumType": "MD5", "checksum": "713aba80d38d371471bdb6357b66a8ff", "customerId": "0012J00002KTQJYQA5", "userId": "TMZIdbKzQGWvvi_04IC9Lg", "storedBytes": 96768, "createdTime": "2022-06-24T04:50:08", "updatedTime": "2022-06-24T04:50:08", "objectState": "AVAILABLE", "bucketId": "5a079a66-e669-4e68-a0a1-6481d1c5bfbc", "tags": [ { "key": "callId", "value": "int-181940b61ea-IMzQUGTvUjs03b07NaxsLqfJ3-phone-00-supertenantcsm01" } ], "shared": false } ] } ``` **Outputs For Next Step:** * id's of the objects we want to download. * objectName => these will be the file names within the zip file after the zip is downloaded ### Pagination This is controlled by `pageKey` and `limit` * `limit` is the number of records to return per page. * `pageKey` is the offset from the beginning of the returned content. Start at zero, if there are multiple pages then the returned pageKey will be the input for the next request. **Pagination Example** Assuming there will be 245 records in total. With an initial input of `pageKey=0&limit=100` the returned meta data will be ```json { "lastPage": false, "pageKey": 100, "pageSize": 100, "content": [ ] } ``` The request for the next page would be `pageKey=100&limit=100` the pageKey has been incremented to the value returned, the following result would be. ```json { "lastPage": false, "pageKey": 200, "pageSize": 100, "content": [ ] } ``` The request for the next page would be `pageKey=200&limit=100` the pageKey has been incremented to the value returned, the following result would be. ```json { "lastPage": true, "pageKey": 300, "pageSize": 45, "content": [ ] } ``` Note: the `lastPage` is now true indicating that there are no more records and the `pageSize` is 45 which is less than the requested `limit` of 100, be aware the pageKey HAS INCREMENTED and should NOT be used to determine if the last page has been reached. ## 4. Create Bulk Download ### Parameters **Method: POST** #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | region | ✓ | The region the objects are in. **Must be one of the regions returned by the Regions request** | us-east | | version | ✓ | The current version is `v3` | v3 | #### Body Array of object ids to be downloaded ```json [ "58bc5748-339b-43c2-ad15-572a35dc3aad", "b6aca571-ebe2-40ed-b553-24b8dc3bc035" ] ``` ### Create Download Request ```bash curl --location --request POST 'https://api.8x8.com/storage/{region}/v3/bulk/download/start' \ --header 'Authorization: Bearer {access_token}' \ --header 'Content-Type: application/json' \ --data-raw '[ "58bc5748-339b-43c2-ad15-572a35dc3aad", "b6aca571-ebe2-40ed-b553-24b8dc3bc035" ]' ``` ### Create Download Response Assuming success, the return will give information on the zipName of the download to be created, the status should be `NOT_STARTED` or `DONE` if it has completed very quickly **Response:** ```json { "zipName": "0bccb889-09f1-4092-a4d7-d1b8c05c0c31.zip", "status": "NOT_STARTED" } ``` **Outputs For Next Step:** * status * zipName ## 5. Check Download Status Check for the download status until the status equals DONE (or an error status..), most use cases are not time critical so leave a sensible delay between polling ( 15-30 seconds perhaps) ### Parameters **Method: POST** #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | region | ✓ | The region the objects are in. **Must be one of the regions returned by the Regions request** | us-east | | version | ✓ | The current version is `v3` | v3 | | zipName | ✓ | the `zipName` returned in the create download request | 0bccb889-09f1-4092-a4d7-d1b8c05c0c31.zip | ### Check Download Status Request ```bash curl --location --request GET 'https://api.8x8.com/storage/{region}/v3/bulk/download/status/0bccb889-09f1-4092-a4d7-d1b8c05c0c31.zip' \ --header 'Authorization: Bearer access_token' \ --header 'Content-Type: application/json' ``` ### Check Download Status Response ```json { "zipName": "0bccb889-09f1-4092-a4d7-d1b8c05c0c31.zip", "status": "DONE" } ``` **Outputs For Next Step:** * status * zipName ## 6. Download Zip File Once the status is `DONE` then we can download the content ### Parameters **Method: POST** #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer kfjdfi3jfopajdkf93fa9pjfdoiap | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | region | ✓ | The region the objects are in. **Must be one of the regions returned by the Regions request** | us-east | | version | ✓ | The current version is `v3` | v3 | | zipName | ✓ | the `zipName` returned in the create download request | 0bccb889-09f1-4092-a4d7-d1b8c05c0c31.zip | ### Download Zip File Request ```bash curl --location --request GET 'https://api.8x8.com/storage/{region}/v3/bulk/download/0bccb889-09f1-4092-a4d7-d1b8c05c0c31.zip' \ --header 'Authorization: Bearer {access_token}' \ --header 'Content-Type: application/json' ``` ### Download Zip File Response **Response:** In the case of callcenterrecording the response will be a zip file containing 1 mp3 per object Id The mp3 files will be named per the objectName from step 3. Example file names within zip file: * int-181940b61ea-IMzQUGTvUjs03b07NaxsLqfJ3-phone-00-supertenantcsm01.mp3 * int-1819420cbe2-sHtEBArUkmmtmyczfbcDGWMwl-phone-00-supertenantcsm01.mp3 > 🚧 **The meta data and content type vary for each object type** > > The Zip file will contain the contents of the objects and the file names within the zip file will be the objectName. It is possible to request objects of multiple types in a single download so the content type/file type need not be the same. > > Example: If you downloaded a callrecording and the transcript of the call then the content would be two files, one with mp3 content and one with json content. > > --- ## Cloud Storage Service Objects ## Filtering by time Filtering by the date when objects where created or when objects were last updated can be done by using *createdTime* or *updatedTime* in the *filter* parameter. These parameters must be specified as unix timestamp in milliseconds. For example, getting all contact center recordings that were created from 2024-09-01 00:00:00 GMT to 2024-10-01 00:00:00 GMT would look like: filter=type==callcenterrecording;**createdTime**=ge=1725148800000;**createdTime**=le=1727740800000 or, for getting same recordings but specifying the time when were last updated filter=type==callcenterrecording;**updatedTime**=ge=1725148800000;**updatedTime**=le=1727740800000 ## Work - Unified Communications ### Work Call Recording filter=type==callrecording ```json { "id": "9984564e-557c-42bf-9b7c-40c7b909d489", "type": "callrecording", "mimeType": "audio/mpeg", "objectName": "ipbx:acmecorp:callrecording:users:117209:1667337742543-1666686434952-117209-+16695555518_E.mp3", "checksumType": "MD5", "checksum": "c03f27f4b1ca0aefea989f1c1b432d9e", "customerId": "0012J00002IZiaKQAT", "userId": "2xEVyEt2SFmZfL3v5UuEdw", "storedBytes": 28224, "createdTime": "2022-11-01T21:22:22", "updatedTime": "2022-11-01T21:22:32", "objectState": "AVAILABLE", "bucketId": "57b3ae7b-da6a-437d-b7eb-8d109f89ef2f", "tags": [ { "key": "guid", "value": "57b3ae7b-da6a-437d-b7eb-8d109f89ef2f" }, { "key": "pbxId", "value": "EhS7MdQXSX2zElaIeTt49Q" }, { "key": "callId", "value": "1666686434952" }, { "key": "ipbxid", "value": "acmecorp" }, { "key": "address", "value": "+16695555518" }, { "key": "endTime", "value": "1667337749032" }, { "key": "branchId", "value": "ZANgBY7WRyOZq_HYzQ48wQ" }, { "key": "duration", "value": "3000" }, { "key": "userName", "value": "Alex Wilber" }, { "key": "callLogId", "value": "3TYkxslLRn6kquFSYoSOxA1666686434952" }, { "key": "direction", "value": "OUTBOUND" }, { "key": "startTime", "value": "1667337742543" }, { "key": "userEmail", "value": "user@example.com" }, { "key": "extensionId", "value": "3TYkxslLRn6kquFSYoSOxA" }, { "key": "extensionNumber", "value": "117209" }, { "key": "media-processed", "value": "true" } ], "shared": false } ``` ### Work Call Recording Transcript > 🚧 **This is only available if the user has Conversation IQ.** > > filter=type==transcription;sourceObjectType==callrecordingchannel There will be 2 objects per call. the speaker tag will identify the party 8x8 user(owner)External Party ```json "tags": [ { "key": "speaker", "value": "owner" } ] ``` ```json "tags": [ { "key": "speaker", "value": "external" } ] ``` ### Work Voicemail file filter=type==voicemail ## 8x8 Contact Center ### Contact Center Call Recording Call Recording for Contact Center calls. Stereo recordings. filter=type==callcenterrecording ### Contact Center Call Recording Transcript filter=type==transcription;sourceObjectType==callcenterrecording ```json { "id": "6cf3260a-d4f2-4fba-8240-53633634713f", "type": "callcenterrecording", "mimeType": "audio/mpeg", "objectName": "int-1781d39f76d-vFuLcjeUpdFzcAnYOl3nC0Rh0-phone-00-acmecorp01.mp3", "checksumType": "MD5", "checksum": "27b75edcb0305a6a6a87d60a1e01f1a4-1", "customerId": "0012J00002KTQJzzzz", "userId": "eqhDvk_OQJWHKP8Qdzzzzz", "storedBytes": 3813912, "createdTime": "2021-03-10T18:02:08", "updatedTime": "2021-03-10T18:02:59", "objectState": "AVAILABLE", "bucketId": "d53d0b69-936d-42df-8232-ccc86a309eee", "tags": [ { "key": "callId", "value": "int-1781d39f76d-vFuLcjeUpdFzcAnYOl3nC0Rh0-phone-00-acmecorp01" }, { "key": "ipbxid", "value": "acmecorp" }, { "key": "address", "value": "Cell Phone CA" }, { "key": "agentId", "value": "eqhDvk_OQJWHKP8Qdzzzzz" }, { "key": "branchId", "value": "mnvMZsd9Rt6YbInURyVzzzz" }, { "key": "calleeId", "value": "12025553989" }, { "key": "callerId", "value": "4085559901" }, { "key": "duration", "value": "1271000" }, { "key": "mediaUrl", "value": "R202103101740420023.wav" }, { "key": "tenantId", "value": "acmecorp01" }, { "key": "agentName", "value": "Khang Glynn" }, { "key": "direction", "value": "INBOUND" }, { "key": "queueName", "value": "Level 3 Support" }, { "key": "startTime", "value": "1615398042234" }, { "key": "calleeName", "value": "Khang Glynn" }, { "key": "callerName", "value": "Cell Phone CA" }, { "key": "channelName", "value": "Main Support" }, { "key": "queueNumber", "value": "1819" }, { "key": "holdDuration", "value": "0" }, { "key": "callSnippetId", "value": "" }, { "key": "transactionId", "value": "10733" }, { "key": "extensionNumber", "value": "600032" }, { "key": "billingTelephoneNumber", "value": "14405558010" } ], "shared": false } ``` ### Post Call Survey The result of the post call survey. Note this data is also available in Post Call Survey API. filter=type==postcallsurvey ```json { "id": "e8430fd2-bf6e-4b95-a6e7-6229066567c3", "type": "postcallsurvey", "mimeType": "application/json", "objectName": "int-18419b888db-G7VBDUQreh0xPFMLVd8n9uedI-phone-00-acmecorp01", "checksumType": "MD5", "checksum": "62418dea13bbc7e403b5df4b5c9a672d", "customerId": "0012J00002KTQJzzzz", "userId": "64oyEUb_Sk6bxVB9P5zzzz", "storedBytes": 567, "createdTime": "2022-10-27T13:53:54", "updatedTime": "2022-10-27T13:54:30", "objectState": "AVAILABLE", "bucketId": "32c0786c-96c6-405e-bce7-74d1b468a763", "tags": [ { "key": "pcsId", "value": "int-18419b888db-G7VBDUQreh0xPFMLVd8n9uedI-phone-00-acmecorp01" }, { "key": "callId", "value": "int-18419b888db-G7VBDUQreh0xPFMLVd8n9uedI-phone-00-acmecorp01" }, { "key": "agentId", "value": "ag64oyEUb_Sk6bxVB9Pzzzz" }, { "key": "tenantId", "value": "acmecorp01" } ], "shared": false } ``` ### Post Call Survey Voice Comment Recording Voice comment left as part of post call survey. filter=type==voicecommentrecording ```json { "id": "1d6005d8-4a25-43cc-baa2-1c32a62d4737", "type": "voicecommentrecording", "mimeType": "audio/wav", "objectName": "int-183e7c02084-Tl4kWT4RnOa5VeOArggrZjBcd-phone-00-acmecorp01-q1.wav", "checksumType": "MD5", "checksum": "f8c528a23eb15160aaebe6e4faf81750", "customerId": "0012J00002KTQJzzz", "userId": "ag64oyEUb_Sk6bxVB9P5zzz", "storedBytes": 61484, "createdTime": "2022-10-17T21:01:51", "updatedTime": "2022-10-17T21:01:54", "objectState": "AVAILABLE", "bucketId": "cfedae99-2010-473b-8530-8c6c1bd74fec", "tags": [ { "key": "pcsId", "value": "int-183e7c02084-Tl4kWT4RnOa5VeOArggrZjBcd-phone-00-acmecorp01" }, { "key": "callId", "value": "int-183e7c02084-Tl4kWT4RnOa5VeOArggrZjBcd-phone-00-acmecorp01" }, { "key": "agentId", "value": "ag64oyEUb_Sk6bxVB9P5zzz" }, { "key": "tenantId", "value": "acmecorp01" }, { "key": "pcsQuestionNumber", "value": "1" }, { "key": "transcription-processed", "value": "true" } ], "shared": false } ``` ### Post Call Survey Voice Comment Recording Transcription filter=type==transcription;sourceObjectType==voicecommentrecording ```json { "id": "bb94a730-a66c-4c19-af88-cc2395e66da4", "type": "transcription", "mimeType": "application/json", "objectName": "int-183b3b3c9e1-7ippeG63cFx6CqobUd8q69SqO-phone-00-acmecorp01-q9.json", "checksumType": "MD5", "checksum": "0f267cbdf609350f73af7e5fb452d615", "customerId": "0012J00002KTQJzzz", "userId": "AQDqmvKiRbG4ekigNSzzz", "storedBytes": 486, "createdTime": "2022-10-07T18:28:55", "updatedTime": "2022-10-07T18:28:57", "objectState": "AVAILABLE", "bucketId": "11ea975d-bc29-4c61-8c3a-eb8dde5458f1", "tags": [ { "key": "pcsId", "value": "int-183b3b3c9e1-7ippeG63cFx6CqobUd8q69SqO-phone-00-acmecorp01" }, { "key": "callId", "value": "int-183b3b3c9e1-7ippeG63cFx6CqobUd8q69SqO-phone-00-acmecorp01" }, { "key": "result", "value": "ok" }, { "key": "agentId", "value": "AQDqmvKiRbG4ekigNSzzz" }, { "key": "duration", "value": "1" }, { "key": "language", "value": "en-US" }, { "key": "provider", "value": "voci" }, { "key": "sourceId", "value": "0b52a701-f20f-4691-b035-e5c8e9cd78af" }, { "key": "tenantId", "value": "acmecorp01" }, { "key": "sourceObjectType", "value": "voicecommentrecording" }, { "key": "pcsQuestionNumber", "value": "9" } ], "shared": false } ``` ### CC Voicemail Recording filter=type==callcentervoicemail ```json { "id": "10b17f97-baff-4610-8f19-70f3f2c0abc9", "type": "callcentervoicemail", "mimeType": "audio/mpeg", "objectName": "int-1843bd9685e-x5zrSPDXGaiqinf9oMFkpUbau-vmail-00-acmecorp01.mp3", "checksumType": "MD5", "checksum": "6f9ccf8d85a6e21296757ae001d70490", "customerId": "0012J00002KTzzzz", "userId": "aiYxR4vVSuCcfajOqwzzzz", "storedBytes": 539712, "createdTime": "2022-11-09T16:30:52", "updatedTime": "2022-11-09T16:31:36", "objectState": "AVAILABLE", "bucketId": "c62f39e5-da8f-4ff7-8f47-da44578e0d31", "tags": [ { "key": "callId", "value": "int-1843bd9685e-x5zrSPDXGaiqinf9oMFkpUbau-vmail-00-acmecorp01" }, { "key": "ipbxid", "value": "acmecorp" }, { "key": "address", "value": "DEF" }, { "key": "agentId", "value": "aiYxR4vVSuCcfajOqwzzzz" }, { "key": "branchId", "value": "mnvMZsd9Rt6YbInURyzzzz" }, { "key": "calleeId", "value": "" }, { "key": "callerId", "value": "" }, { "key": "duration", "value": "67000" }, { "key": "mediaUrl", "value": "094e5cf7-85d4-4f6d-a6a2-b8c8aaa1633e_1663085857.wav" }, { "key": "tenantId", "value": "acmecorp01" }, { "key": "agentName", "value": "" }, { "key": "direction", "value": "INBOUND" }, { "key": "queueName", "value": "Sales Voice Mail" }, { "key": "startTime", "value": "1668011452227" }, { "key": "agentEmail", "value": "" }, { "key": "calleeName", "value": "" }, { "key": "callerName", "value": "DEF" }, { "key": "channelName", "value": "13125555066" }, { "key": "queueNumber", "value": "571" }, { "key": "holdDuration", "value": "" }, { "key": "callSnippetId", "value": "" }, { "key": "transactionId", "value": "874" }, { "key": "originalCallId", "value": "int-18337a3bdfa-WawHeGWwrRIvpTdmz2p027kdj-phone-00-acmecorp01" }, { "key": "extensionNumber", "value": "" }, { "key": "billingTelephoneNumber", "value": "" } ], "shared": false } ``` ### CC Voicemail Recording Transcript filter=type==transcription;sourceObjectType==callcentervoicemail ```json { "id": "6a3ba4d7-c221-4c23-8d50-7e9e5fdb7625", "type": "transcription", "mimeType": "application/json", "objectName": "int-18729fc3e63-3siHRMg9PX50ZpArizpitQOIQ-vmail-00-supertenantcsm01.json", "checksumType": "MD5", "checksum": "9a5006469effa23d4489e8ee2f969f8b", "customerId": "0012J00002KTQJaaaa", "userId": "AopPWJ1BR82b9UZwKDaaaa", "storedBytes": 2062, "createdTime": "2023-03-28T21:10:41", "updatedTime": "2023-03-28T21:10:45", "objectState": "AVAILABLE", "bucketId": "71870394-33ac-4f82-b6a7-8e6f59f8fc09", "tags": [ { "key": "callId", "value": "int-18729fc3e63-3siHRMg9PX50ZpArizpitQOIQ-vmail-00-supertenantcsm01" }, { "key": "ipbxid", "value": "acmecorp" }, { "key": "result", "value": "ok" }, { "key": "address", "value": "M,DENNIS" }, { "key": "agentId", "value": "agAopPWJ1BR82b9UZwKDaaaa" }, { "key": "branchId", "value": "IhKgvB0kQb6Jvv1pbG1Apg" }, { "key": "calleeId", "value": "" }, { "key": "callerId", "value": "" }, { "key": "duration", "value": "18" }, { "key": "language", "value": "en-US" }, { "key": "mediaUrl", "value": "c6ae7dfd-f81d-4eac-8949-94279f50b548_1680036586.wav" }, { "key": "provider", "value": "voci" }, { "key": "sourceId", "value": "f0f1b23c-a8e5-4cf7-9d6e-05e2b4cf2562" }, { "key": "tenantId", "value": "acmecorp01" }, { "key": "agentName", "value": "" }, { "key": "direction", "value": "INBOUND" }, { "key": "queueName", "value": "Marketing Voicemail" }, { "key": "startTime", "value": "1680037811842" }, { "key": "agentEmail", "value": "" }, { "key": "calleeName", "value": "" }, { "key": "callerName", "value": "M,DENNIS" }, { "key": "channelName", "value": "CVFP Main Number" }, { "key": "queueNumber", "value": "480" }, { "key": "holdDuration", "value": "" }, { "key": "callSnippetId", "value": "" }, { "key": "transactionId", "value": "1868" }, { "key": "originalCallId", "value": "int-18729fb4334-vxp1mdHa16jYDAv7UP3HxmXTe-phone-00-acmecorp01" }, { "key": "extensionNumber", "value": "" }, { "key": "sourceObjectType", "value": "callcentervoicemail" }, { "key": "billingTelephoneNumber", "value": "" } ], "shared": false } ``` ### CC Screen Recording filter=type==screenrecording ```json { "id": "9e58ab51-360e-46db-b508-6e5ba6bc3700", "type": "screenrecording", "mimeType": "video/mp4", "objectName": "int-1845ea47cdb-7uK6Uj2BHUMubNY7OOKsgiyjM-phone-00-acmecorp01.mp4", "checksumType": "MD5", "checksum": "619924e1c5202f83340b149d14f6d1d4", "customerId": "0012J00002KTQJYaaa", "userId": "whzJ0NOwTdWid_JP6DpFYw", "storedBytes": 3907847, "createdTime": "2022-11-09T23:15:12", "updatedTime": "2022-11-09T23:15:18", "objectState": "AVAILABLE", "bucketId": "a06fef64-3160-4b87-93bb-83ec67fb983b", "tags": [ { "key": "callId", "value": "int-1845ea47cdb-7uK6Uj2BHUMubNY7OOKsgiyjM-phone-00-acmecorp01" }, { "key": "agentId", "value": "agwhzJ0NOwTdWid_JP6zzzz" }, { "key": "duration", "value": "542000" }, { "key": "tenantId", "value": "acmecorp01" }, { "key": "startTime", "value": "1668035167650" }, { "key": "startedBy", "value": "vcc-agui" }, { "key": "identifier", "value": "NA12_acmecorp01" }, { "key": "transactionId", "value": "7434" }, { "key": "recording_start_time", "value": "20221109T230607" } ], "shared": false } ``` ### CC Agent Notes Notes added to transactions by agents. Object contains text. filter=type==agentnotes ```json { "id": "c5381c7d-28b5-4bbd-9f87-cbb47adc613e", "type": "agentnotes", "mimeType": "text/plain", "objectName": "int-18486b7d9b9-9rNMbf8CSyBum7opdCSIsgFdr-phone-00-supertenantcsm01.agentnotes", "checksumType": "MD5", "checksum": "76ede5ffb605855281abb5173e1da70a", "customerId": "0012J00002Kzzzzzzz", "userId": "AD21EBR1RhuV2TNDizzzz", "storedBytes": 39, "createdTime": "2022-11-17T17:51:46", "updatedTime": "2022-11-17T17:54:01", "objectState": "AVAILABLE", "bucketId": "26572b1c-e844-4c44-a6b6-3bd8a2df7dff", "tags": [ { "key": "callId", "value": "int-18486b7d9b9-9rNMbf8CSyBum7opdCSIsgFdr-phone-00-acmecorp01" }, { "key": "ipbxid", "value": "acmecorp" }, { "key": "tenantId", "value": "acmecorp01" } ], "shared": false } ``` ### AI/ML Sentiment Analysis Scores Getting sentiment scores from AI/ML sentiment analysis. filter=type==sentimentScore ```json { "id": "daa0b61e-c36c-402c-90f3-a3f3fdf1139b", "type": "sentimentScore", "mimeType": "application/json", "objectName": "int-1924c64e00a-zMCJ1IqScHpzxu2uMdOcDVwH8-phone-03-acmecorp01.json", "checksumType": "MD5", "checksum": "5b643ff0678e10ab643e21e6b7b2a251", "customerId": "0012J00002Kzzzzzzz", "userId": "AD21EBR1RhuV2TNDizzzz", "storedBytes": 2732, "createdTime": "2024-10-02T08:44:30", "updatedTime": "2024-10-02T08:44:30", "objectState": "AVAILABLE", "bucketId": "58252ded-dd6d-4453-a4c4-994bc4b21ace", "tags": [ { "key": "callId", "value": "int-1924c64e00a-zMCJ1IqScHpzxu2uMdOcDVwH8-phone-03-acmecorp01" }, { "key": "ipbxid", "value": "acmecorp" }, { "key": "tenantId", "value": "acmecorp01" }, { "key": "sourceObjectType", "value": "transcription" }, { "key": "contentObjectType", "value": "callcenterrecordingchannel" }, { "key": "sentimentProcessed", "value": "true" } ], "shared": false } ``` ### AI/ML summary Getting an AI/ML generated summary from a transcript (voice or digital) filter=type==summary ```json { "id": "651dc994-27aa-43cc-8f43-301fa6458639", "type": "summary", "mimeType": "application/json", "objectName": "int-1921de567b1-7zrCRtowKo5rmiXNVI0G4PLOE-phone-03-acmecorp01.json", "checksumType": "MD5", "checksum": "54007f2dc30d9d6df5628e18d996ba3a", "customerId": "0012J00002Kzzzzzzz", "userId": "AD21EBR1RhuV2TNDizzzz", "storedBytes": 571, "createdTime": "2024-09-23T08:02:10", "updatedTime": "2024-09-23T08:02:16", "objectState": "AVAILABLE", "bucketId": "34c8c69e-8a43-4ea0-9996-51ada9e06c25", "tags": [ { "key": "callId", "value": "int-1921de567b1-7zrCRtowKo5rmiXNVI0G4PLOE-phone-03-acmecorp01" }, { "key": "result", "value": "ok" }, { "key": "tenantId", "value": "acmecorp01" }, { "key": "sourceObjectId", "value": "8d1bfa3c-3519-4823-a131-646bde4cd271" }, { "key": "sourceObjectType", "value": "transcription" }, { "key": "contentObjectType", "value": "callcenterrecordingchannel" } ], "shared": false } ``` --- ## Customer 360 ## Overview The Customer 360 API provides unified access to a customer's interaction history and insights across all 8x8 contact center channels. Given a customer identity — email address, phone number, contact ID, or account ID — the API returns a list of interactions along with aggregated sentiment analysis and topic insights derived from those interactions. ## Base URLs The API is available in four regions. Use the base URL corresponding to the region where your tenant is provisioned: * Phoenix (US): `https://api.8x8.com/cidp-customer-360/us` * London (UK): `https://api.8x8.com/cidp-customer-360/uk` * Toronto (Canada): `https://api.8x8.com/cidp-customer-360/ca` * Sydney (Australia): `https://api.8x8.com/cidp-customer-360/ap` The Customer 360 API supports the following endpoints: * **GET** `/v1/public/tenants/{tenantId}/interactions-insight` — Retrieve interaction insights * **GET** `/v1/public/tenants/{tenantId}/transcript-summaries` — Retrieve transcript summaries ### 1. Get Interaction Insights This GET method retrieves interaction history and aggregated insights for a specific customer identity within an optional time range. #### Headers | Name | Required | Description | Example | | --------- | -------- | ------------------------------------------------------------------------------------------------ | ----------------------------- | | x-api-key | ✓ | API key from the [8x8 Admin Console](/analytics/docs/how-to-get-api-keys) | eght_Abcdhfakdlbdfsjkbskzkmxl | #### Path Parameters | Name | Required | Description | Example | | -------- | -------- | ------------------------------------------------------ | -------------------- | | tenantId | ✓ | The tenant identifier. Must belong to your customer account. | acvcc1652172111112801 | #### Search Strategies You must provide exactly one identity field per request. The supported search strategies are: | Strategy | Required Fields | Optional Fields | Forbidden Fields | | ----------- | --------------------- | --------------- | ---------------- | | Contact ID | `contactId`, `crmId` | — | — | | Account ID | `accountId` | `crmId` | — | | Email | `email` | — | `crmId` | | Phone Number| `phoneNumber` | — | `crmId` | Only the native CRM is supported. Set `crmId` to `native`. #### Query Parameters | Name | Required | Description | Example | | ----------- | -------- | ---------------------------------------------------------------------------------------------------- | ---------------------------- | | email | ☐ | Customer email address. Cannot be used with `crmId`. | `customer@example.com` | | phoneNumber | ☐ | Customer phone number in E.164 format. Cannot be used with `crmId`. | +12065551234 | | contactId | ☐ | Contact identifier. Must be provided together with `crmId`. | contact-123 | | accountId | ☐ | Account identifier. `crmId` is optional when using this field. | account-456 | | crmId | ☐ | CRM identifier. Required with `contactId`, optional with `accountId`, forbidden with email/phone. Only the native CRM is supported. | native | | startTime | ☐ | Start of the time range in ISO-8601 format with timezone. Defaults to 1 year before `endTime`. | 2025-01-01T00:00:00-05:00 | | endTime | ☐ | End of the time range in ISO-8601 format with timezone. Defaults to current time. | 2025-08-01T00:00:00-05:00 | #### Full Request Example ```bash curl --location 'https://api.8x8.com/cidp-customer-360/us/v1/public/tenants/acvcc1652172111112801/interactions-insight?email=customer%40example.com' \ --header 'x-api-key: eght_Abcdhfakdlbdfsjkbskzkmxl' ``` #### Response ```json { "interactions": [ { "interactionId": "int-abc123", "mediaIdentifier": "customer@example.com", "mediaType": "EMAIL", "direction": "INBOUND", "productType": "CC", "startedAt": 1724400000000, "endedAt": 1724403600000, "sentiment": "POSITIVE", "topics": [ { "topic": "billing", "matches": 2 } ], "wrapUpCodes": ["resolved"], "queueName": "Support Queue", "outcomeLabel": "Resolved", "interactionLabels": ["vip"], "emailSubject": "Billing inquiry", "signals": [] }, { "interactionId": "int-def456", "mediaIdentifier": "customer@example.com", "mediaType": "CHAT", "chatType": "WebChat", "direction": "INBOUND", "productType": "CC", "startedAt": 1724500000000, "endedAt": 1724503600000, "sentiment": "NEUTRAL", "topics": [], "wrapUpCodes": ["happy"], "queueName": "inbound chat", "outcomeLabel": "Handled", "interactionLabels": ["Queued", "Handled"], "title": "Account upgrade request", "signals": [] } ], "insights": { "aggregatedSentiments": { "totalInteractions": 2, "aggregatedSentiment": { "aggregatedCustomerSentiment": "POSITIVE", "aggregatedAgentSentiment": "NEUTRAL", "aggregatedOverallSentiment": "POSITIVE" } }, "aggregatedTopics": { "totalInteractions": 1, "topicFrequency": [ { "topicName": "billing", "categoryName": "Finance", "interactionsMatchedCount": 1, "percentageMatched": 50.0 } ] } } } ``` > 📘 **Maximum interactions** > > The API returns a maximum of 50 interactions per request within the specified time range. #### Response Body Fields **interactions** | Field | Description | | ------------------ | --------------------------------------------------------------------------------------------------- | | `interactionId` | Unique identifier for the interaction. | | `contactId` | CRM contact identifier associated with the interaction. | | `mediaIdentifier` | The customer email or phone number used in this interaction. | | `mediaType` | Channel type: `PHONE`, `EMAIL`, `CHAT`, or `VOICEMAIL`. | | `chatType` | Sub-type for chat interactions, e.g. `WHATSAPP`. | | `direction` | Direction of the interaction: `INBOUND` or `OUTBOUND`. | | `productType` | 8x8 product that handled the interaction: `CC` (Contact Center), `UC` (Unified Communications), or `ENGAGE`. | | `startedAt` | Interaction start time as Unix epoch milliseconds. | | `endedAt` | Interaction end time as Unix epoch milliseconds. | | `sentiment` | Overall sentiment for this interaction: `POSITIVE`, `NEUTRAL`, or `NEGATIVE`. | | `topics` | Topics detected in the interaction. See **topics** table below. | | `wrapUpCodes` | Agent wrap-up codes applied at the end of the interaction. | | `queueName` | Name of the queue that handled the interaction. | | `outcomeLabel` | Outcome label assigned to the interaction. | | `interactionLabels`| Labels applied to the interaction. | | `title` | AI-generated title summarizing the interaction (when available). | | `emailSubject` | Subject line of the email (present for `EMAIL` interactions only). | | `departmentName` | Name of the department that handled the interaction. Reserved for future use. | | `signals` | Detected signals from the interaction. Reserved for future use; currently returns an empty list. | **topics** | Field | Description | | --------- | -------------------------------------------------------- | | `topic` | Name of the detected topic. | | `matches` | Number of times this topic was detected in the interaction. | **insights.aggregatedSentiments** | Field | Description | | ---------------------------- | ----------------------------------------------------------------------------------------------- | | `totalInteractions` | Number of interactions included in the sentiment aggregation. | | `aggregatedSentiment` | Object containing the aggregated sentiment breakdown. See **aggregatedSentiment** table below. | **insights.aggregatedSentiments.aggregatedSentiment** | Field | Description | | ---------------------------- | -------------------------------------------------------------- | | `aggregatedCustomerSentiment`| Overall customer sentiment: `POSITIVE`, `NEUTRAL`, or `NEGATIVE`. | | `aggregatedAgentSentiment` | Overall agent sentiment: `POSITIVE`, `NEUTRAL`, or `NEGATIVE`. | | `aggregatedOverallSentiment` | Combined overall sentiment: `POSITIVE`, `NEUTRAL`, or `NEGATIVE`. | **insights.aggregatedTopics** | Field | Description | | ---------------------------- | ----------------------------------------------------------------------------------------------- | | `totalInteractions` | Number of interactions included in the topic aggregation. | | `topicFrequency` | List of topics with frequency data. See **topicFrequency** table below. | **insights.aggregatedTopics.topicFrequency** | Field | Description | | -------------------------- | ------------------------------------------------------------------- | | `topicName` | Name of the topic. | | `categoryName` | Category the topic belongs to. | | `interactionsMatchedCount` | Number of interactions where this topic was detected. | | `percentageMatched` | Percentage of total interactions where this topic was detected. | > 📘 **Speech Analytics data** > > `sentiment`, `topics`, and aggregated insights are only populated when the Speech Analytics license is enabled for the tenant. --- ### 2. Get Transcript Summaries This GET method retrieves AI-generated transcript summaries for one or more interactions by their interaction IDs. #### Headers | Name | Required | Description | Example | | --------- | -------- | ------------------------------------------------------------------------------------------------ | ----------------------------- | | x-api-key | ✓ | API key from the [8x8 Admin Console](/analytics/docs/how-to-get-api-keys) | eght_Abcdhfakdlbdfsjkbskzkmxl | #### Path Parameters | Name | Required | Description | Example | | -------- | -------- | ------------------------------------------------------ | -------------------- | | tenantId | ✓ | The tenant identifier. Must belong to your customer account. | acvcc1652172111112801 | #### Query Parameters | Name | Required | Description | Example | | ------------- | -------- | ---------------------------------------------------------------------------------------------------- | ----------- | | interactionId | ✓ | One or more interaction IDs. Repeat the parameter for batch retrieval. | int-abc123 | #### Full Request Example Single interaction: ```bash curl --location 'https://api.8x8.com/cidp-customer-360/us/v1/public/tenants/acvcc1652172111112801/transcript-summaries?interactionId=int-abc123' \ --header 'x-api-key: eght_Abcdhfakdlbdfsjkbskzkmxl' ``` Batch retrieval (multiple interaction IDs): ```bash curl --location 'https://api.8x8.com/cidp-customer-360/us/v1/public/tenants/acvcc1652172111112801/transcript-summaries?interactionId=int-abc123&interactionId=int-def456' \ --header 'x-api-key: eght_Abcdhfakdlbdfsjkbskzkmxl' ``` #### Response ```json { "summaries": [ { "interactionId": "int-abc123", "summaryObjectId": "obj-xyz789", "content": { "id": "summary-001", "result": "Customer called about a billing discrepancy. Agent verified the charge and issued a credit. Customer confirmed satisfaction.", "status": "success", "type": "summary", "duration": 185.5 }, "metadata": { "createdAt": "2025-08-15T14:30:00", "sizeBytes": 512 } } ], "partialFailures": [ { "interactionId": "int-def456", "reason": "Summary not found" } ] } ``` > 📘 **Maximum interaction IDs** > > The API accepts a maximum of 50 interaction IDs per request. > 📘 **Partial failures** > > When some interaction IDs cannot be resolved, the API returns a 200 response with successfully retrieved summaries in `summaries` and unresolved IDs in `partialFailures`. Possible reasons: `Summary not found` (no summary exists for this interaction) or `Summary not available` (temporary retrieval failure). #### Response Body Fields **summaries** | Field | Description | | ----------------- | -------------------------------------------------------- | | `interactionId` | Interaction identifier. | | `summaryObjectId` | Unique identifier of the summary object in storage. | | `content` | The summary content. See **content** table below. | | `metadata` | Metadata about the summary. See **metadata** table below.| **content** | Field | Description | | ---------- | -------------------------------------------------------------- | | `id` | Summary content identifier. | | `result` | The AI-generated transcript summary text. | | `status` | Processing status of the summary (e.g. `success`). | | `type` | Content type (e.g. `summary`). | | `duration` | Duration of the interaction in seconds. | **metadata** | Field | Description | | ----------- | ---------------------------------------------- | | `createdAt` | Timestamp when the summary was created. | | `sizeBytes` | Size of the summary content in bytes. | **partialFailures** | Field | Description | | --------------- | ----------------------------------------------------------------------------------------------- | | `interactionId` | Interaction identifier for which the summary could not be retrieved. | | `reason` | Reason the summary could not be retrieved: `Summary not found` or `Summary not available`. | --- ## Post Call Survey > 📘 **Post-call survey reporting has moved under the umbrella of Contact Center reporting.** > > For general access and structure guidelines, please refer to the documentation on the [CC Historical Analytics Summary Reports](/analytics/docs/cc-historical-analytics-summary-report) and the [CC Historical Analytics Detailed Reports](/analytics/docs/cc-historical-analytics-detailed-report). > > The [API specification](/analytics/reference/cc-historical-report-create) is also available. > > ## Creating reports Post-call survey data is available via the CC API (v7 only) in either summary or detailed form: | Report name | Type | Description | | --- | --- | --- | | surveys-summary | Summary | Overall metrics for the time period, grouped together by time according to the `granularity` parameter, and by an additional `groupBy` parameter. | | survey-questions-summary | Summary | Question-by-question aggregates for one or more surveys. | | detailed-reports-survey | Detailed | Tabulated individual survey response data. | Full details are available at the [`GET /report-types` endpoint](/analytics/reference/cc-historical-analytics-report-types). Please see the corresponding documentation for the [CC Historical Analytics Summary Reports](/analytics/docs/cc-historical-analytics-summary-report) and the [CC Historical Analytics Detailed Report](/analytics/docs/cc-historical-analytics-detailed-report) respectively for more specific documentation on how to create reports using these endpoints, and how to collect the data they specify. > ❗️ **Post-call survey data is only available in CC API version 7 or later** > > ## Fields and terms ### Survey lifecycle metrics Surveys go through many states as customers interact with them. We track those states, and report the data in the survey-summary report using this vocabulary: | Field name | Definition | | --- | --- | | Offered | The survey/question was presented to the user by IVR | | OptedIn | The user affirmatively interacted with the IVR to start the survey | | Started | Some amount of the survey/question audio was presented to the user | | Completed | The user has completed the question interaction, or in the context of a survey, completed all questions and any final script segments | > 📘 **Agent-assisted surveys** > > For agent assisted surveys and standalone surveys (surveys with no IVR component), we are not able to offer metrics for `Offered` or `OptedIn`, because there was no IVR interaction, which is how we detect these states. > > ### Question metrics In the question-summary report, we collect data on how users specifically interact with individual questions. | **Field name** | **Definition** | | --- | --- | | Answered | The user submitted a response to the question | | Skipped | The user did not submit a response to the question before a timeout | | HungUp | The user ended the call during the question audio or response period | This report also has a notion of `valid` and `invalid` inputs, and aggregates the keypad user input received in the survey in these separate categories. ### Score metrics | Field Name | Definition | | --- | --- | | achievableScore | The maximum score for a question. Or, in the case of a summary context, the sum of all maximum scores in the aggregation. | | actualScore | The score provided by the customer in response to the question. Or, in the case of a summary context, the sum of all provided scores in the aggregation | | score | the ratio of `actualScore / achievableScore` | This report also has a notion of `valid` and `invalid` inputs, and aggregates the keypad user input received in the survey in these separate categories. ### Detail metrics Detailed data for surveys and questions is available via our detailed reports API. Below, the fields the response may contained are described in more detail: | **Field name** | **Description** | | --- | --- | | AgentIds | Ids of agents that participated in the associated call | | AgentNames | Names of agents from `agentIds` field | | AnswerDigit | In the case of a keypad input question with a response, the digit pressed; otherwise, null | | AnswerType | one of "Recorded", "Skipped", "Valid", "Invalid" | | CallerName | Caller ID name for non-agent member of the associated call | | CallerNumber | Caller ID number for non-agent member of the associated call | | EndTime | Timestamp for the end of the survey | | InteractionIds | Interactions associated with this survey or question | | QuestionId | Unique identifier for a particular question | | QuestionTitle | Title of a particular question | | QuestionType | One of "scale", "yesNo", "voiceComment" | | QueueIds | Queue ids that the associated call interacted with | | QueueNames | Names of the queues from the `queueIds` field | | ScaleMax | Maximum valid score for a question | | ScaleMin | Minimum valid score for a question | | StartTime | Timestamp for the start of the survey | | SurveyDuration | Duration of the survey | | SurveyId | ID of the survey | | SurveyName | Name of the survey | | SurveyScorePercentage | ratio of the user's inputted score over the total achievable score for numeric questions | | SurveyType | One of "stayOnCall", "callback" | | TransactionIds | List of transaction ids associated with a particular survey or question. For more information about transaction IDs, [please review the documentation.](https://support.8x8.com/contact-center/8x8-contact-center/agents/how-to-get-transaction-ids-in-8x8-contact-center) | | actualScore | The score provided by the customer in response to all numerical questions on the survey | | achievableScore | The maximum score for all validly answered numerical questions on the survey | | agentGroupIds | The list of all agent group ids that any agents involved with the survey are in | | agentGroupNames | The name of all agent groups from the agentGroupIds field | | channelId | The channel associated with the call the survey was on | | surveyIsDeleted | Boolean value for if the survey is currently deleted or not | | voiceRecordingUuid | The uuid value associated with a question (if the question is a voice comment) | ### Grouping and filtering Survey-summary PCS reports are automatically grouped by survey, and question-summary reports are similarly grouped by survey and additionally by question. These grouped results can be filtered by providing survey ids, queue ids, or agent ids as specified by the documentation (link) in an additional `filters` array placed inside the `groupBy` object. --- ## [PILOT] End-to-End Journey API ## Introduction The Journey API provides a consolidated view of customer interactions belonging to the same journey. These interactions may happen on one or multiple 8x8 platforms, including Contact Center (CC), Unified Communications (UC), and Engage. Using the API users can retrieve journey data and detailed transition information, enabling end-to-end tracking of customer journeys regardless of transfers between systems or agents. This API is particularly valuable for organizations with complex flows that span multiple systems, where customers may be transferred between formal contact center agents and back-office operations. ## Business Value The Journey API solves critical business challenges: - **Unified Customer Journey Tracking:** Track complete customer journeys across CC, UC, and Engage in a single view - **Transfer Pattern Analysis:** Understand how calls are transferred between systems and agents - **Comprehensive Metrics:** Access consolidated metrics like handling time, queue wait time, and outcomes across all platforms - **Detailed Transition History:** Examine every state a customer interaction passed through Instead of working with disconnected reporting systems, organizations can now build comprehensive reports and dashboards in third-party BI tools with a complete view of all customer interactions. ## Authentication All API requests require an x-api-key header for authentication. Obtain your x-api-key from the Admin Console application. Required Request Header: ```bash x-api-key: your-api-key-value ``` ## Base URLs | Region | Base URL | | ------------- |-------------------------------------------| | United States | `https://api.8x8.com/cidp/journey/api` | | Europe | `https://api-eu.8x8.com/cidp/journey/api` | ## API Endpoints Overview The API provides two main synchronous endpoints: 1. **Journeys** (`/v1/journeys/search`): Provides aggregated journey data across all platforms, including comprehensive metrics like handling time, queue wait time, and outcomes. 2. **Transitions** (`/v1/transitions/search`): Provides detailed information about each state transition within a journey, allowing you to track the exact journey path. ## API Endpoints ### Journeys Endpoint ```http POST /v1/journeys/search ``` Retrieves journey data based on the specified criteria. #### Request Headers | Name | Required | Description | Example | | ------------ | -------- | -------------------------------------- | -------------------- | | Content-Type | ✓ | Set to application/json | `application/json` | | x-api-key | ✓ | API key from Admin Console application | `your-api-key-value` | #### Request Body | Name | Required | Description | Example | | --------------- | -------- | ----------------------------------------------------------------------------------- | ---------------------- | | dateRange.start | ✓ | Start datetime in ISO 8601 format with timezone designator | `2025-03-01T00:00:00Z` | | dateRange.end | ✓ | End datetime in ISO 8601 format with timezone designator | `2025-03-10T00:00:00Z` | | filters | ☐ | Set of filter objects with name and values | See filters section | | displayTimezone | ✓ | IANA timezone display name - the desired display timezone value for the time fields | `Europe/Bucharest` | | limit | ☐ | Maximum number of records to return (default: 100) | `50` | | nextPageCursor | ☐ | Cursor for pagination from previous response | `encoded-cursor-value` | | sortField | ☐ | Field to sort by (default: `TIME`) | `TIME` | | sortDirection | ☐ | Sort direction, either `ASC` or `DESC` (default: `ASC`) | `DESC` | > ⚠️ **Important: Date Range Filtering Behavior** > > The `dateRange` filter works as follows: > > - **For Journeys**: The API returns all journeys that have a `time` within the specified date range (between `start` and `end`) > - **For Transitions**: The API returns ALL transitions belonging to journeys that started within the date range > > This means: > > - A journey is included if it started within the date range > - All transitions for included journeys are returned, even if some transitions occurred after the `end` date > - The `start` and `end` parameters define the search interval, NOT the duration of individual journeys or transitions > > **Example**: If you search for journeys between 9:00 AM and 10:00 AM, you'll get all journeys that started in that hour, along with ALL their transitions - even if some transitions happened at 11:00 AM or later. > ⚠️ **Timerange Limit** > > **Note:** The maximum allowed timerange for any data retrieval request is **60 days**. > > For optimal performance consider using a timerange of 1 day or less. If you need to analyze data over a longer period, break your requests into multiple segments, each covering no more than 7 days. #### Example Request ```json { "dateRange": { "start": "2025-05-19T08:00:00+03:00", "end": "2025-05-19T16:00:00+03:00" }, "filters": [ { "name": "pbxNames", "values": [ "yourPbxName" ] }, { "name": "tenantIds", "values": [ "yourTenantId" ] } ], "displayTimezone": "Europe/Bucharest", "limit": 50, "sortDirection": "DESC" } ``` #### Example Response ```json { "data": [ { "time": "2025-03-10T13:33:42+02:00", "finishedTime": "2025-03-10T13:38:15+02:00", "journeyId": "7b2429440d15183af1044dd47f1d447d", "interactions": [ { "id": "int-196e53e066e-QNePASHISrp0D65MVgaNJYS13-phone-01-emeriaeurope01", "direction": "Inbound", "type": "Contact Center" }, { "id": "int-196e53e066e-QNePASHISrp0D65MVgaNJYS13-ai-studio-01", "direction": "Inbound", "type": "AI Studio" } ], "agents": [ { "id": "ag-123", "name": "John Doe", "email": "john.doe@example.com", "loginId": "jdoe", "department": "Customer Support", "group": { "id": "101", "name": "ungroup" }, "type": "HUMAN" } ], "contact": { "name": "+443335565567", "phoneNumber": "+449988776655", "email": "john.doe@8x8.com" }, "entryPoint": { "type": "user", "id": "user-789", "name": "Jane Smith", "phoneNumber": "+1234567890", "extension": "1001", "email": "jane.smith@example.com", "loginId": "jsmith", "department": "Sales", "pbx": "mainPbx", "tenantId": "8x8", "site": null }, "direction": "Inbound", "transfersCompleted": 0, "forwardedToQueue": 0, "forwardedToRingGroup": 0, "forwardedToScript": 2, "holdDuration": 0, "mediaTypes": [ "Phone" ], "outcome": "Handled", "origin": { "type": "user", "id": "user-789", "name": "Jane Smith", "phoneNumber": null, "extension": "1036", "email": "jane.smith@example.com", "loginId": "jsmith", "department": "Sales", "pbx": "mainPbx" }, "pbxNames": [], "queues": [], "ringGroups": [], "scheduleHours": ["Open", "Closed"], "schedules": [ { "id": 107, "tag": "Ferie", "tenantId": "cexpbx01", "result": "open" } ], "tenantIds": [ "8x8" ], "wrapUpCodes": [], "outboundPhoneCodes": [] } ], "nextPageCursor": "ZW5jbLW5leHQtRlZC1jdXJzb3ItZm9ycGFnZQ==", "totalElements": 150 } ``` ### Transitions Endpoint ```http POST /v1/transitions/search ``` Retrieves transition data based on the specified criteria. #### Request Headers | Name | Required | Description | Example | | ------------ | -------- | -------------------------------------- | -------------------- | | Content-Type | ✓ | Set to application/json | `application/json` | | x-api-key | ✓ | API key from Admin Console application | `your-api-key-value` | #### Request Body Same structure as Journeys endpoint, but with transition-specific filters. #### Example Request ```json { "dateRange": { "start": "2025-05-21T00:00:00+03:00", "end": "2025-05-21T23:59:59+03:00" }, "filters": [ { "name": "transitions.name", "values": [ "TRANSFER" ] } ], "displayTimezone": "Europe/Bucharest", "limit": 100 } ``` #### Example Response ```json { "data": [ { "journeyId": "3015ccfd5ebf8b2dcde38045ed30bc7", "transitions": [ { "time": "2025-05-21T12:09:23.312+03:00", "name":"STARTED", "interactionId": "int-196f21ac6ef-d5eed30bc738045-phone-02-8x8", "agents": [], "previousAgents": [], "previousQueue": null, "previousRingGroup": null, "queue": null, "ringGroup": null, "duration": 0, "externalNumber": null, "channel": null, "mediaType": "Phone" }, { "time": "2025-05-21T12:09:23.312+03:00", "name":"IN_SCRIPT", "interactionId": "int-196f21ac6ef-d5eed30bc738045-phone-02-8x8", "agents": [], "previousAgents": [], "previousQueue": null, "previousRingGroup": null, "queue": null, "ringGroup": null, "duration": 9206, "externalNumber": null, "channel": null, "scripts": [ { "id": 541, "name": "example_script_name", "tenantId": "cexpbx01" } ], "mediaType": "Phone" }, ["..."], { "time": "2025-05-21T12:10:12.143+03:00", "name":"TRANSFER", "interactionId": "int-196f21ac6ef-d5eed30bc738045-phone-02-8x8", "agents": [ { "id": "ag-456", "name": "Jack Pott", "email": "jack.pott@example.com", "loginId": "jpott", "department": "Technical Support", "group": null, "type": "HUMAN" } ], "previousAgents": [ { "id": "ag-123", "name": "Marsha Mellow", "email": "marsha.mellow@example.com", "loginId": "mmellow", "department": "Customer Support", "group": null, "type": "HUMAN" } ], "previousQueue": { "id": "queue-101", "name": "Service Client", "extension": "2001" }, "previousRingGroup": null, "queue": null, "ringGroup": null, "duration": 0, "externalNumber": "+441138413014", "channel": { "id": "ch-voice-1", "name": "Voice", "tenantId": "testterrafoncia01" }, "mediaType": "Phone" }, ["..."] ] } ], "nextPageCursor": null, "totalElements": 1 } ``` ## Filters Filters allow you to narrow down the data returned by the API based on specific criteria. Different filter types are available for journeys and Transitions endpoints. By default, the API returns all journeys/transitions belonging to your customer account across all PBXes and tenants. To return only journeys for a specific PBX, add a `pbxNames` filter. To return only journeys for a specific tenant, add a `tenantIds` filter. All filters use AND logic — when multiple filters are provided, only results matching **all** of them are returned. ### Journey Filters | Filter Name | Description | Example Values | |--------------------------------|------------------------------------------|--------------------------------------------------------------------------| | `agents.group.id` | Filter by agent group IDs | `["group1", "group2"]` | | `agents.group.name` | Filter by agent group names | `["Team A", "Team B"]` | | `agents.name` | Filter by agent names | `["John Doe", "Jane Smith"]` | | `agents.site.id` | Filter by agent site ID | `["site-123"]` | | `agents.site.name` | Filter by agent site name | `["London Office"]` | | `agents.type` | Filter by agent type (`HUMAN` or `AI`) | `["HUMAN"]`, `["AI"]` | | `contact.email` | Filter by contact email | `["jane.doe@example.com"]` | | `contact.name` | Filter by contact name | `["Jane Doe"]` | | `contact.phoneNumber` | Filter by contact phone number | `["+1234567890"]` | | `direction` | Filter by journey direction | `["Inbound", "Outbound", "Internal"]` | | `entryPoint.extension` | Filter by entry point extension | `["1001"]` | | `entryPoint.id` | Filter by entry point ID | `["ep-789"]` | | `entryPoint.name` | Filter by entry point name | `["Main Support Line"]` | | `entryPoint.phoneNumber` | Filter by entry point phone number | `["053244122"]` | | `entryPoint.site.id` | Filter by entry point site ID | `["site-123"]` | | `entryPoint.site.name` | Filter by entry point site name | `["London Office"]` | | `entryPoint.type` | Filter by entry point type | `["cc-channel", "ring-group", "call-queue", "auto-attendant", "user"]` | | `interactions.id` | Filter by interaction ID | `["interaction-123"]` | | `journeyId` | Filter by journey ID | `["journey-123"]` | | `mediaTypes` | Filter by media types | `["phone", "chat", "email"]` | | `origin.extension` | Filter by origin extension | `["1001"]` | | `origin.id` | Filter by origin ID | `["user-789"]` | | `origin.name` | Filter by origin name | `["Jane Smith"]` | | `origin.phoneNumber` | Filter by origin phone number | `["+1234567890"]` | | `origin.site.id` | Filter by origin site ID | `["site-123"]` | | `origin.site.name` | Filter by origin site name | `["Berlin Office"]` | | `origin.type` | Filter by origin type | `["user"]` | | `outcome` | Filter by journey outcome | `["Handled", "Abandoned", "EndedInScript", "Other", "UnknownOutcome"]` | | `outboundPhoneCodes.listName` | Filter by outbound phone code list name | `["JohnDoeInbound"]` | | `outboundPhoneCodes.name` | Filter by outbound phone code name | `["No queue"]` | | `outboundPhoneCodes.shortCode` | Filter by outbound phone code short code | `["Unt1"]` | | `pbxNames` | Filter by PBX names | `["pbx1", "pbx2"]` | | `queues.extension` | Filter by queue extension | `["2001"]` | | `queues.id` | Filter by queue ID | `["queue-123"]` | | `queues.name` | Filter by queue names | `["Customer Support Queue"]` | | `queues.site.id` | Filter by queue site ID | `["site-123"]` | | `queues.site.name` | Filter by queue site name | `["Paris Office"]` | | `ringGroups.extension` | Filter by ring group extension | `["3001"]` | | `ringGroups.id` | Filter by ring group ID | `["rg-456"]` | | `ringGroups.name` | Filter by ring group names | `["Sales Ring Group"]` | | `ringGroups.site.id` | Filter by ring group site ID | `["site-123"]` | | `ringGroups.site.name` | Filter by ring group site name | `["Berlin Office"]` | | `tenantIds` | Filter by tenant IDs | `["tenant1", "tenant2"]` | | `wrapUpCodes` | Filter by wrap-up codes | `["Service Call", "Support Call"]` | ### Transition Filters | Filter Name | Description | Example Values | |-------------------------------------------|-----------------------------------------|---------------------------| | `journeyId` | Filter by journey ID | `["journey-123"]` | | `pbxNames` | Filter by PBX names | `["pbx1"]` | | `tenantIds` | Filter by tenant IDs | `["tenant1"]` | | `transitions.agents.name` | Filter by agent names | `["John Doe"]` | | `transitions.agents.site.id` | Filter by agent site ID | `["site-123"]` | | `transitions.agents.site.name` | Filter by agent site name | `["Toronto Office"]` | | `transitions.agents.type` | Filter by agent type (`HUMAN` or `AI`) | `["HUMAN"]`, `["AI"]` | | `transitions.interactionId` | Filter by interaction ID | `["interaction-123"]` | | `transitions.name` | Filter by transition name | `["TALKING", "TRANSFER"]` | | `transitions.previousAgents.name` | Filter by previous agents | `["Marsha Mellow"]` | | `transitions.previousAgents.site.id` | Filter by previous agent site ID | `["site-123"]` | | `transitions.previousAgents.site.name` | Filter by previous agent site name | `["Toronto Office"]` | | `transitions.previousAgents.type` | Filter by previous agent type (`HUMAN` or `AI`) | `["HUMAN"]`, `["AI"]` | | `transitions.previousQueue.extension` | Filter by previous queue extension | `["2001"]` | | `transitions.previousQueue.id` | Filter by previous queue ID | `["queue-123"]` | | `transitions.previousQueue.name` | Filter by previous queue name | `["Support S2"]` | | `transitions.previousQueue.site.id` | Filter by previous queue site ID | `["site-123"]` | | `transitions.previousQueue.site.name` | Filter by previous queue site name | `["Vienna Office"]` | | `transitions.previousRingGroup.extension` | Filter by previous ring group extension | `["3001"]` | | `transitions.previousRingGroup.id` | Filter by previous ring group ID | `["rg-456"]` | | `transitions.previousRingGroup.name` | Filter by previous ring group name | `["Sales Ring Group"]` | | `transitions.previousRingGroup.site.id` | Filter by previous ring group site ID | `["site-123"]` | | `transitions.previousRingGroup.site.name` | Filter by previous ring group site name | `["Madrid Office"]` | | `transitions.queue.extension` | Filter by queue extension | `["2001"]` | | `transitions.queue.id` | Filter by queue ID | `["queue-123"]` | | `transitions.queue.name` | Filter by queue name | `["Support S1"]` | | `transitions.queue.site.id` | Filter by queue site ID | `["site-123"]` | | `transitions.queue.site.name` | Filter by queue site name | `["Rome Office"]` | | `transitions.ringGroup.extension` | Filter by ring group extension | `["3001"]` | | `transitions.ringGroup.id` | Filter by ring group ID | `["rg-456"]` | | `transitions.ringGroup.name` | Filter by ring group name | `["Sales Ring Group"]` | | `transitions.ringGroup.site.id` | Filter by ring group site ID | `["site-123"]` | | `transitions.ringGroup.site.name` | Filter by ring group site name | `["Amsterdam Office"]` | | `transitions.autoAttendant.id` | Filter by auto attendant ID | `["aa-123"]` | | `transitions.autoAttendant.name` | Filter by auto attendant name | `["Main Auto Attendant"]` | | `transitions.autoAttendant.extension` | Filter by auto attendant extension | `["1040"]` | | `transitions.autoAttendant.site.id` | Filter by auto attendant site ID | `["site-123"]` | | `transitions.autoAttendant.site.name` | Filter by auto attendant site name | `["London Office"]` | > The `autoAttendant` field is populated when the underlying event is a UC auto attendant: on FORWARD/TRANSFER it is the auto attendant the call is routed to, and on IN_SCRIPT it is the auto attendant whose script is running. ### Filter Format Filters are provided as an array of objects in the request body: ```json { "filters": [ { "name": "agents.name", "values": [ "John Doe", "Marsha Mellow" ] }, { "name": "mediaTypes", "values": [ "phone" ] } ] } ``` ## Pagination The API uses cursor-based pagination to efficiently navigate through large result sets. Here's how it works: 1. Make an initial request with a desired `limit` value in the request body (default is 100) 2. The response includes a `nextPageCursor` if there are more records available 3. To retrieve the next page, include the `nextPageCursor` in your next request body 4. Continue this process until `nextPageCursor` is null, indicating no more pages ### Pagination Example Initial request: ```json { "dateRange": { "start": "2025-05-19T00:00:00Z", "end": "2025-05-19T23:59:59Z" }, "displayTimezone": "UTC", "limit": 100 } ``` Response with next page cursor: ```json { "data": [ /* 100 records */ ], "nextPageCursor": "encoded-cursor-value", "totalElements": 250 } ``` Next page request: ```json { "dateRange": { "start": "2025-05-19T00:00:00Z", "end": "2025-05-19T23:59:59Z" }, "displayTimezone": "UTC", "limit": 100, "nextPageCursor": "encoded-cursor-value" } ``` Last page response: ```json { "data": [ /* remaining records */ ], "nextPageCursor": null, "totalElements": 250 } ``` ## Sorting The API supports sorting of results through two parameters in the request body: - `sortField`: Specifies which field to sort by (default: `TIME`) > ℹ️ **Note:** Currently, TIME is the only available sortField. - `sortDirection`: Specifies the sort order, either `ASC` (ascending) or `DESC` (descending) (default: `ASC`) Example: ```json { "dateRange": { "start": "2025-05-19T00:00:00Z", "end": "2025-05-19T23:59:59Z" }, "displayTimezone": "UTC", "sortField": "TIME", "sortDirection": "DESC" } ``` ## Data Models ### Journey Data Model The Journeys Endpoint provides a consolidated view of all customer interactions belonging to the same journey across all 8x8 platforms. Each journey record includes the following structure: | Field | Type | Description | |--------------------------|----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `time` | ISO8601 date | When the journey started. | | `finishedTime` | ISO8601 date | When the journey ended. `null` if the journey is still in progress. | | `journeyId` | string | The journeyId field returned by the Journeys Endpoint uniquely identifies and aggregates all interactions associated with a particular customer journey across all platforms. Use this journeyId to correlate and analyze comprehensive interaction data for a specific journey. | | `interactions` | Interaction\[] | Array of interaction objects representing the interactions belonging to the journey. These interactions can originate from CC, UC or Engage platforms. | | `agents` | Agent\[] | Array of agent objects involved in handling the journey | | `contact` | Contact | Contact object containing name, phone number, and email who interacts with the organization. As an example for inbound phone interactions this is the caller, and for outbound ones this is the callee. | | `entryPoint` | entryPoint | entryPoint object is populated only for inbound journeys and identifies where the interaction first enters the organization (e.g. a contact center channel, or a unified communication ring group). For outbound journeys this object is `null`. | | `transfersCompleted` | int | Number of warm & cold transfers during the journey | | `forwardedToQueue` | int | Number of automated forwards to a CC or UC queue during the journey | | `forwardedToRingGroup` | int | Number of automated forwards to a Ring Group during the journey | | `forwardedToScript` | int | Number of automated forwards to a UC AA + CC script/IVR | | `holdDuration` | long | Total hold time in milliseconds | | `mediaTypes` | string\[] | Types of media in the journey (phone, chat, email, etc.) | | `origin` | origin | Origin object is populated only for UC outbound calls and identifies what agent made the call | | `outcome` | string | Outcome of the journey (e.g. abandoned, handled, forwarded to VM). | | `direction` | string | Direction of the journey (e.g. Inbound, Outbound, Internal). | | `pbxNames` | string\[] | PBX names | | `queues` | Queue\[] | Array of queue objects used in the journey | | `ringGroups` | RingGroup\[] | Array of ring group objects used in the journey | | `scheduleHours` | string\[] | Distinct array of values for all the IVR `scheduleHours` nodes of the journey. | | `schedules` | Schedule\[] | CC schedule nodes the journey was evaluated against. Always an array — empty when the journey was evaluated against no schedule node. See **Schedule Object** below. | | `tenantIds` | string\[] | Tenant IDs | | `wrapUpCodes` | string\[] | Wrap-up codes applied to the journey | | `outboundPhoneCodes` | OutboundPhoneCode\[] | Outbound phone codes - populated only for CC agent-initiated outbound interactions. | #### Nested Object Structures **Interaction Object:** ```json { "id": "string", "direction": "string", "type": "string" } ``` | Field | Type | Description | |-------------|--------|-------------------------------------------------------------------------------------------------| | `id` | string | Unique identifier of the interaction | | `direction` | string | Direction of the interaction (e.g. `Inbound`, `Outbound`) | | `type` | string | Type of the interaction. Known values: `Contact Center`, `Unified Communication`, `AI Studio`. | **Agent Object:** ```json { "id": "string", "name": "string", "email": "string", "loginId": "string", "department": "string", "group": { "id": "string", "name": "string" }, "site": { "id": "string", "name": "string" }, "type": "HUMAN" } ``` | Field | Type | Description | |--------------|------------|-----------------------------------------------------------------------------------------------------------------------------------| | `id` | string | Unique identifier of the agent/user | | `name` | string | Display name of the agent/user | | `email` | string | Email address of the agent/user. May be `null` if user data enrichment is unavailable or disabled. | | `loginId` | string | Login ID of the agent/user. May be `null` if user data enrichment is unavailable or disabled. | | `department` | string | Department of the agent/user. May be `null` if user data enrichment is unavailable or disabled. | | `group` | AgentGroup | Agent group the agent belongs to. May be `null` for UC users (no Contact Center group). | | `site` | Site | Site (branch/office) the agent/user belongs to. May be `null` if the agent has no site assigned. See **Site Object** below. | | `type` | string | Agent type. `HUMAN` for CCA/UA agents, `AI` for AI Studio bots. | **Contact Object:** ```json { "name": "string", "phoneNumber": "string", "email": "string" } ``` **Queue Object:** ```json { "id": "string", "name": "string", "extension": "string", "site": { "id": "string", "name": "string" } } ``` | Field | Type | Description | |-------------|--------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `id` | string | Unique identifier of the queue | | `name` | string | Display name of the queue | | `extension` | string | Queue extension number. **Note:** Contact Center queues do not have extensions — this field will be `null` for Contact Center queues. Only Unified Communications queues have extension values. | | `site` | Site | Site (branch/office) the queue belongs to. | **RingGroup Object:** ```json { "id": "string", "name": "string", "extension": "string", "site": { "id": "string", "name": "string" } } ``` | Field | Type | Description | |-------------|--------|-------------------------------------------------------------------------------------------------------------------| | `id` | string | Unique identifier of the ring group | | `name` | string | Display name of the ring group | | `extension` | string | Ring group extension number | | `site` | Site | Site (branch/office) the ring group belongs to. | **Site Object:** {#site-object} The Site object identifies the physical site (branch/office) associated with a Unified Communications service. The Site is exposed wherever those entities appear in the API: - on `agents[].site` (journey response) — site of the agent/user that handled the journey - on `queues[].site` and `ringGroups[].site` (journey response) — site of the UC queue/ring group - on `origin.site` (journey response) — site of the UC user that initiated an outbound call - on `transitions[].agents[].site` and `transitions[].previousAgents[].site` (transition response) — site of the agent/user at each state change - on `transitions[].queue.site`, `transitions[].previousQueue.site`, `transitions[].ringGroup.site`, `transitions[].previousRingGroup.site` (transition response) - on `entryPoint.site` when the entry point's `type` is `call-queue`, `ring-group`, or `auto-attendant` In all locations the field is `null` when the underlying entity has no site assigned. Filters are available on every `*.site.id` and `*.site.name` path — see [Journey Filters](#journey-filters) and [Transition Filters](#transition-filters). ```json { "id": "string", "name": "string" } ``` | Field | Type | Description | |--------|--------|------------------------------| | `id` | string | Unique identifier of the site | | `name` | string | Display name of the site | **OutboundPhoneCode Object:** The outboundPhoneCodes field is populated only for CC agent-initiated outbound interactions. ```json { "listName": "string", "name": "string", "shortCode": "string" } ``` | Field | Description | |-------------|----------------------------------------------------------| | `name` | Menu text of the outbound phone code in Contact Manager | | `shortCode` | Short code identifier of the outbound phone code | | `listName` | Name of the outbound phone code list | **Schedule Object:** A schedule node a journey was evaluated against in a CC script. `schedules` is always an array on the journey — an empty array when the journey was evaluated against no schedule node. ```json { "id": 107, "tag": "Ferie", "tenantId": "cexpbx01", "result": "open" } ``` | Field | Type | Description | |----------------|--------|--------------------------------------------------------------------------------------------------| | `id` | int64 | Unique identifier of the schedule | | `tag` | string | Tag of the schedule node (IVR object tag). May be `null`. | | `tenantId` | string | Tenant identifier the schedule belongs to | | `result` | string | Customer-facing evaluation result: `"open"`, `"closed"`, or `"1"`–`"6"` (a selected choice path) | **Channel Object:** The Channel object identifies the communication channel associated with a transition. It is exposed on the Transitions endpoint only; the Journeys endpoint does not return a `channel` field. The whole object is `null` for transitions that have no associated channel (e.g. `STARTED`, `WAITING`, `FINISHED`). When present, all three fields are populated. ```json { "id": "string", "name": "string", "tenantId": "string" } ``` | Field | Type | Description | |------------|--------|---------------------------------------------------| | `id` | string | Unique identifier of the channel | | `name` | string | Display name of the channel | | `tenantId` | string | Tenant identifier the channel belongs to | **Script Object:** The Script object identifies a CC IVR script the interaction visited. It is exposed on the Transitions endpoint only and is populated on `IN_SCRIPT` transitions; on every other transition state the `scripts` array is empty (`[]`), never `null`. ```json { "id": 541, "name": "example_script_name", "tenantId": "cexpbx01" } ``` | Field | Type | Description | |------------|---------|---------------------------------------------------| | `id` | integer | Unique identifier of the script (int64) | | `name` | string | Display name of the script | | `tenantId` | string | Tenant identifier the script belongs to | **entryPoint Object:** The entryPoint object is populated only for inbound journeys and identifies where the interaction first enters the organization (e.g. a contact center channel, or a unified communication ring group). ```json { "type": "string", "id": "string", "name": "string", "phoneNumber": "string", "extension": "string", "email": "string", "loginId": "string", "department": "string", "pbx": "string", "tenantId": "string", "site": { "id": "string", "name": "string" } } ``` | Field | Type | Description | |---------------|--------|------------------------------------------------------------------------------------------------------------| | `type` | string | Entry point type. See **entryPoint Type Values** below. | | `id` | string | Unique identifier of the entry point | | `name` | string | Display name of the entry point | | `phoneNumber` | string | Phone number associated with the entry point | | `extension` | string | Extension associated with the entry point | | `email` | string | Email of the entry point. May be `null`. See note on USER-type entry points below. | | `loginId` | string | Login ID of the entry point. Only populated for USER-type. May be `null`. | | `department` | string | Department of the entry point. Only populated for USER-type. May be `null`. | | `pbx` | string | PBX identifier | | `tenantId` | string | Tenant identifier | | `site` | Site | Site associated with the entry point. Only populated when `type` is `call-queue`, `ring-group`, or `auto-attendant`; `null` otherwise. | **entryPoint Type Values:** The `type` field in the entryPoint object is an enum that identifies the type of entry point. It can have one of the following values: - **cc-channel**: The inbound interaction enters through a contact center channel (e.g. Customer Support, Sales Queue, Email Support, etc.) - **call-queue**: The inbound interaction enters through a unified communication call queue - **ring-group**: The inbound interaction enters through a ring group - **auto-attendant**: The inbound interaction enters through a unified communication auto-attendant - **user**: The inbound interaction targets a unified communication user directly, bypassing contact center channels. Entry points of this type include additional user details (`email`, `loginId`, `department`) reflecting current Admin Console configuration **origin Object:** The origin object is populated only for UC outbound journeys and identifies what agent initiated the call. ```json { "type": "user", "id": "string", "name": "string", "phoneNumber": "string", "extension": "string", "email": "string", "loginId": "string", "department": "string", "pbx": "string", "site": { "id": "string", "name": "string" } } ``` | Field | Type | Description | |---------------|--------|---------------------------------------------------------------------------------------------------------------------------------------------------| | `type` | string | Origin type. Currently only `user` (the UC user who initiated the outbound call). | | `id` | string | Unique identifier of the origin user | | `name` | string | Display name of the origin user | | `phoneNumber` | string | Phone number associated with the origin user | | `extension` | string | Extension associated with the origin user | | `email` | string | Email address of the origin user. May be `null` if user data enrichment is unavailable or disabled. | | `loginId` | string | Login ID of the origin user. May be `null` if user data enrichment is unavailable or disabled. | | `department` | string | Department of the origin user. May be `null` if user data enrichment is unavailable or disabled. | | `pbx` | string | PBX identifier | | `site` | Site | Site (branch/office) the origin user belongs to. May be `null` if the user has no site assigned. See **Site Object** above. | ### Transition Data Model The Transitions Endpoint provides detailed information about each state change within a journey, allowing users to track the exact journey path. Journeys are identified with the *journeyId*. Each transition record includes: | Field | Type | Description | |---------------------| ------------ |------------------------------------------------------------| | `time` | ISO8601 date | When the transition occurred | | `journeyId` | string | ID of the parent journey | | `name` | string | Name of the transition (e.g., WAITING, TALKING, TRANSFER) | | `interactionId` | string | ID of the interaction | | `agents` | Agent\[] | Array of agent objects (if applicable) | | `previousAgents` | Agent\[] | Array of previous agent objects (if applicable) | | `previousQueue` | Queue | Previous queue object (if applicable) | | `previousRingGroup` | RingGroup | Previous ring group object (if applicable) | | `queue` | Queue | Queue object (if applicable) | | `ringGroup` | RingGroup | Ring group object (if applicable) | | `duration` | long | Duration of this specific transition state in milliseconds | | `externalNumber` | string | External number (if applicable) | | `channel` | Channel | Communication channel associated with the transition. `null` when the transition has no associated channel. See **Channel Object** below. | | `scripts` | Script\[] | Ordered list of CC IVR scripts visited by the interaction. Populated only on `IN_SCRIPT` transitions. See **Script Object** below. | | `mediaType` | string | Media type (phone, chat, email, etc.) | ### Transition States A journey can progress through multiple transition states: - **STARTED**: An interaction with a customer has started. Information about the customer is included in the `contact` object of the journey. - **OUTBOUND_STARTED**: An outbound interaction initiated by a UC user or CC agent has started. The `duration` captures the time from initiation until the customer answers (transition to `TALKING`) or the call is abandoned/disconnected (transition to `FINISHED`). - **IN_SCRIPT**: The customer is interacting with a script like an IVR script or an auto-attendant. - **WAITING**: The customer is waiting to be handled by an agent (e.g. waiting in a queue or while the phones in a ring group are ringing etc.). - **TALKING**: An agent is interacting with the customer. - **HOLD**: An agent has put the call on hold (e.g. while the agent is preparing a transfer). - **FORWARD**: The customer interaction is routed by the system to another destination. Destinations can be an *agent*, a *queue/ring group* or a *phone number*. The properties *agents*, *queue*, *ringGroup* and *externalNumber* contain the destination of the forward. The properties *previousAgents*, *previousQueue* and *previousRingGroup* contain the origin of the forward. - **TRANSFER**: The customer interaction is transferred by an agent handling the customer to another destination. Destinations can be another *agent*, a *queue/ring group* or another *phone number*. The properties *agents*, *queue*, *ringGroup* and *externalNumber* contain the destination of the transfer. The properties *previousAgents*, *previousQueue* and *previousRingGroup* contain the origin of the transfer. - **FINISHED**: The customer journey has ended. ### Journey Outcomes The `outcome` field in the Journeys API response summarizes the result of a customer journey. #### Possible Outcome Values | Value | Description | | ---------------- | --------------------------------------------------------------------------------------- | | `Handled` | The journey was successfully handled by an agent or user. | | `Abandoned` | The journey was abandoned by the customer before being handled by an agent or user. | | `EndedInScript` | The journey ended in an IVR script or auto-attendant without reaching an agent or user. | | `Other` | The journey ended with an outcome not covered by the above categories. | | `UnknownOutcome` | The outcome could not be determined from the available data. | ## Error Handling The API returns standard HTTP status codes and an error response body: ```json { "errors": [ { "message": "Error message description", "suggestion": "Suggested action to resolve the error", "url": "https://developer.8x8.com/analytics/docs/end-to-end-journey-api#malformed-request-malformedrequest" } ] } ``` ### Common Errors | HTTP Status Code | Description | | ---------------- | ----------------------------------------- | | 400 | Bad Request - Invalid input parameters | | 401 | Unauthorized - Invalid or missing API key | | 403 | Forbidden - Insufficient permissions | | 404 | Not Found - Resource not found | | 429 | Too Many Requests - Rate limit exceeded | | 500 | Internal Server Error - Server-side error | ## Error Codes This section provides detailed explanations for each error code that the API might return. Each error header is linkable via its anchor for easy navigation within the documentation. ### Malformed Request {#malformedRequest} This error indicates that the request payload is not properly structured. It may be due to invalid JSON syntax or an incorrect structure that does not conform to the API specification. ### Bad Request {#badRequest} A generic error indicating that the request is invalid. This can be caused by missing required fields, incorrect data types, or any other violation of the API’s requirements. ### Date Range Not Null {#dateRangeNotNull} This error is raised when the date range parameter is missing from the request. A valid date range is required to determine the period for which the data should be retrieved. ### Start Date Not Null {#startRangeNotNull} This error occurs when the start date of the date range is not provided. The API requires a valid start date to define the beginning of the data retrieval period. ### End Date Not Null {#endRangeNotNull} This error is returned if the end date of the date range is missing. A valid end date is needed to mark the conclusion of the period for which data is requested. ### End Date Before Start Date {#endDateBeforeStartDate} This error occurs when the provided start date is later than the end date. The API expects the start date to precede the end date to form a valid interval. ### Filters Not Empty {#filtersNotEmpty} This error is triggered when the filters array is empty. At least one filter must be supplied to narrow down the data and make the query meaningful. ### ISO 8061 With Timezone Format {#iso8061WithTzFormat} This error is raised when a date does not conform to the ISO 8601 format with a timezone designator (for example, `2025-03-01T00:00:00Z`). Correct date formatting is required for proper parsing. ### Max Interval {#maxInterval} This error indicates that the interval between the start and end dates exceeds the maximum allowed period. The user should specify a smaller date range to process the request successfully. ### Filter Values Not Empty {#filterValuesNotEmpty} This error is returned when a filter is provided without any associated values. Each filter must include at least one value to effectively narrow down the data. ### Invalid Filter Type {#invalidFilterType} This error occurs when the filter type specified in the request is not among the supported types. Users must ensure that only valid filter types are used. ### Timezone Not Null {#timezoneNotNull} This error is raised when the displayTimezone parameter is missing. A valid IANA timezone identifier must be provided to ensure accurate time-based data processing. ### Invalid Timezone {#invalidTimezone} This error is thrown if the provided timezone does not match any recognized IANA timezone. Users should verify and provide a valid timezone identifier. ### Invalid Sort Direction {#invalidSortDirection} This error occurs when the sort direction is neither `asc` nor `desc`. The API only accepts these two values for sorting order. ### Invalid Sort Field {#invalidSortField} This error is returned when the sort field specified is not supported. At the moment, only the `TIME` field is available for sorting results. ### Invalid Limit {#invalidLimit} This error indicates that the limit parameter is out of the acceptable range (typically between 1 and 1000). Users should adjust the limit to a valid number within the allowed range. ### Invalid Cursor {#invalidCursor} This error is raised when the pagination cursor provided in the request is invalid. The cursor must be the one returned from a previous valid request. ### Cursor And Sort Mismatch {#cursorAndSortMismatch} This error indicates that the sort field or direction associated with the provided cursor does not match the current request parameters. Ensure that the cursor is used with the same sort settings as those in the original response. ### Invalid Endpoint {#invalidApiPath} This error occurs when the requested API endpoint does not exist or is not recognized by the API. Please verify the endpoint against the valid paths listed in the `API Endpoints` section above. ### Invalid Request Method {#invalidRequestMethod} This error is returned when the HTTP method used in the request does not match the expected method for the endpoint. Please ensure you are using the correct HTTP method (e.g., POST) as specified in the `API Endpoints` section above. ## Rate Limiting The API implements rate limiting to protect system resources. By default up to 10 hits are allowed within a 60 seconds sliding window. When rate limits are exceeded, the API returns a 429 status code with a Retry-After header indicating when you can try again. ## Use Cases ### 1. End-to-End Customer Journey Analysis For organizations with complex call flows that span multiple platforms (such as transferred calls between contact center agents and back-office teams), this API provides a complete view of the customer journey. #### Implementation Steps 1. Retrieve journey data: ```http POST /v1/journeys/search ``` ```json { "dateRange": { "start": "2025-05-19T08:00:00+03:00", "end": "2025-05-19T23:00:00+03:00" }, "filters": [ { "name": "pbxNames", "values": [ "mainPbx" ] } ], "displayTimezone": "Europe/Paris", "limit": 50 } ``` 2. For detailed journey analysis, retrieve transition data using the journeyId from the journeys response: ```http POST /v1/transitions/search ``` ```json { "dateRange": { "start": "2025-05-19T08:00:00+03:00", "end": "2025-05-19T23:00:00+03:00" }, "filters": [ { "name": "journeyId", "values": [ "journey-123" ] } ], "displayTimezone": "Europe/Paris", "sortDirection": "ASC" } ``` ### 2. Transfer Pattern Analysis For organizations that want to understand how calls are being transferred between systems and analyze transfer patterns. #### Implementation Steps 1. Retrieve all transfer transitions: ```http POST /v1/transitions/search ``` ```json { "dateRange": { "start": "2025-05-19T00:00:00+03:00", "end": "2025-05-19T23:59:59+03:00" }, "filters": [ { "name": "transitions.name", "values": [ "TRANSFER" ] } ], "displayTimezone": "Europe/Paris", "limit": 100 } ``` ### 3. Queue Performance Analysis For analyzing queue performance across different platforms. #### Implementation Steps 1. Retrieve journey data filtered by queues: ```http POST /v1/journeys/search ``` ```json { "dateRange": { "start": "2025-05-19T00:00:00+03:00", "end": "2025-05-19T23:59:59+03:00" }, "filters": [ { "name": "queues.name", "values": [ "support", "sales", "technical" ] } ], "displayTimezone": "Europe/Paris", "limit": 100 } ``` ## Best Practices 1. **Use appropriate date ranges**: Keep date ranges reasonably small (ideally less than 1 day) to improve performance. For longer historical analysis, consider breaking your requests into multiple segments. 2. **Apply relevant filters**: Use filters to narrow down results and improve response times. 3. **Handle pagination properly**: Always check for the `nextPageCursor` and fetch all pages when needed. 4. **Respect rate limits**: Implement appropriate retry mechanisms with backoff when encountering rate limiting. 5. **Cache results when appropriate**: For frequently accessed data that doesn't change often, consider caching on your side. 6. **Handle errors gracefully**: Check for error responses and retry with backoff for transient errors. ## API Glossary ### Time Representations All times in the API are represented in ISO 8601 format with timezone designator (e.g., `2025-03-01T00:00:00Z`). The displayTimezone specified in your request is only used for displaying purposes. ### Duration Metrics All duration metrics (handling time, wait time, etc.) are provided in milliseconds. ### Media Types - **phone**: Phone interactions - **chat**: Chat interactions - **email**: Email interactions ### Interaction Direction - **inbound**: Customer-initiated interactions - **outbound**: Agent-initiated interactions - **internal**: Internal interactions (e.g., between agents) ### PBX and Tenant IDs PBX names and tenant IDs are used for filtering and represent the 8x8 platform instances your organization uses. ## Further Assistance For additional support or questions about the Journey API, please contact your 8x8 representative or submit a support ticket through the 8x8 support portal. --- ## How to get API Keys There are a number of different Authentication Methods across the suite of XCaaS APIs today. This document will outline how to get API keys for each API and also what the basic Authentication method is once you have your keys. ## Where to Get API Keys | API Description | Key Creation Process | |-------------------------------------------|-----------------------------------------------------------------------------------------| | Contact Center Historical Analytics | [Admin Console](/analytics/docs/how-to-get-api-keys#admin-console-api-key-generation) | | Contact Center Realtime Analytics | [Admin Console](/analytics/docs/how-to-get-api-keys#admin-console-api-key-generation) | | Cloud Storage Service | [Admin Console](/analytics/docs/how-to-get-api-keys#admin-console-api-key-generation) | | Quality Management & Speech Analytics | [Admin Console](/analytics/docs/how-to-get-api-keys#admin-console-api-key-generation) | | Work Analytics | [Admin Console](/analytics/docs/how-to-get-api-keys#admin-console-api-key-generation) | | Audit Records | [Admin console](/analytics/docs/how-to-get-api-keys#admin-console-api-key-generation) | | Customer 360 | [Admin console](/analytics/docs/how-to-get-api-keys#admin-console-api-key-generation) | | Contact Search | [Admin console](/analytics/docs/how-to-get-api-keys#admin-console-api-key-generation) | | Contact Management | [Admin console](/analytics/docs/how-to-get-api-keys#admin-console-api-key-generation) | ## Admin Console API key generation > 🚧 **The "Application Credentials" permission is required to create/manage API Keys in Admin Console** > > This permission is enabled for users of the default Company Admin Role or a custom role can be created for specific users using the application: 8x8 Admin Console and the permission: "Application Credentials". > > To create a "Call Recording & Storage" API Keys, the user must first have the 'Cloud Storage API' assignment. **This assignment must be granted by the Super Admin.** > > [Admin Console](https://admin.8x8.com) provides an ability to create API keys as follows: If you do not have the API Keys option you do not have the correct permission/role. ![API Key Generation](../images/API_Key_Generation.png "API Key Generation Menu") The list of available API keys and an option to create new keys will be presented: ![API Key List](../images/API_Key_List.png "API Key List" ) An App is a set of credentials that have access to a specific set of APIs. You can choose to create as many apps as you wish, and each app can have access to as many or few APIs as appropriate. To create an App: * Give it a name that is meaningful to you, No spaces allowed. * Assign the APIs that are appropriate for the use case (you can modify this later if/as needed) In this example, a single App will have access to Analytics for Contact Center API. ![Create AP App](../images/Create_Analytics_App.png "Create AP App.png") > 📘 **Modifying an existing App** > > If an existing App is modified by adding or removing an API, there can be a brief delay in the changes being globally consistent if the App key/secret are under active use. Changes will replicate and become consistent within a few minutes. > > Clicking the eyeball icon will display the Key and Secret onscreen. Clicking elsewhere on the row will bring a view of the App showing which APIs are enabled and allowing the secret/key to be revealed/copied individually ![API Key Details](../images/Analytics_API_Key_Detail.png "API Key Details.png") Clicking the eyeball icon besides the Key and or Secret will reveal the Key/Secret and allow them to be copied. APIs can be added or removed from the App on this screen The App can be deleted from this screen. --- ## Introduction(3) Welcome to the XCaaS Analytics & Content Developer hub. You'll find comprehensive guides and documentation to help you start working with XCaaS Analytics & Content as quickly as possible, as well as support if you get stuck. Let's jump right in! Analytics and Content will provide access to analytics, recordings and other content you are generating as an XCaaS customer. You will find Guides which describe the APIs and some use cases and [API References](../reference) which allow you to test and try the APIs right from this site. --- ## OAuth Authentication for 8x8 XCaaS APIs A number of 8x8 XCaaS APIs use OAuth authentication. For these APIs the initial step is to generate an `access_token` to be used in the subsequent API calls. **Analytics and Content APIs using OAuth Method:** * [CC Realtime Analytics](/analytics/reference/cc-real-time-get-queues-metrics) * [CC Historical Analytics](/analytics/reference/cc-historical-report-create) * [Cloud Storage Service](/analytics/reference/searchobject) * [Quality Management](/analytics/reference/interactions-count) **Other APIs using OAuth Method:** * [Contact Center Chat](/actions-events/reference/createaccesstoken) > 📘 **You will need a working API key to begin** > > [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > The URL for the OAuth Authentication is: `https://api.8x8.com/oauth/v2/token` ## Authenticate to retrieve access token Using the key and secret from Admin Console as the username and password use Basic Authentication. ### Parameters **Method: POST** #### Headers | Name | Required | Description | Example | | ------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------ | | Authorization | ✓ | [Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication) where username is the value of clientId and the password is the value of secret. Example shows value for clientId=myclientIdsecret=nevertellanyone | Basic bXljbGllbnRJZDpuZXZlcnRlbGxhbnlvbmU= | | Content-Type | ✓ | Specify form content type to pass the grant_type in the body | application/x-www-form-urlencoded | #### Body > 📘 **Table contains the values for the x-www-form-urlencoded body** > > | Name | Required | Description | Example | | ---------- | -------- | ---------------------------- | ------------------ | | grant_type | ✓ | Must be `client_credentials` | client_credentials | ### Authentication Request ```bash curl --location --request POST 'https://api.8x8.com/oauth/v2/token' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --header 'Authorization: Basic base64encode({clientId}:{secret})' --data-urlencode 'grant_type=client_credentials' ``` ### Authentication Response **Response** ```json { "access_token": "{{TOKEN_VALUE}}", "token_type": "BearerToken", "api_product_list": "[CE-PCS-Product, CE-RCS-Product, Chat, QM - API, analytics product, analytics realtime-api, chat-gateway, storage, vcc]", "status": "approved", "scope": "", "refresh_token_expires_in": "0", "expires_in": "1799", "refresh_count": "0", "developer.email": "deprecated", "issued_at": "1683045181383", "client_id": "deprecated", "api_product_list_json": [ "CE-PCS-Product", "CE-RCS-Product", "Chat", "QM - API", "analytics product", "analytics realtime-api", "chat-gateway", "storage", "vcc" ] } ``` ## Outputs that are used in the subsequent API **issued_at** : Epoch time of when the token was issued **expires_in**: Number of seconds before this `access_token` will expire. If your use case will leverage the `access_token` for a period that could exceed the lifetime of the token ensure that your code either handles an error based on the token expiration OR requests a new token before the current token expires. We recommend against getting a new token for every request as this will result in added duration and processing on both sides. **access_token**: This is the token that will be passed into subsequent API calls as a Bearer Token. In the example above the `access_token` is `3yKcgVwWCJM14dXxKDBAEDGcythJ` `access_token`: is used to populate `Authorization` header in the subsequent request set to `Bearer {access_token}` (Space between Bearer and the access_token) Example using the `access_token` above to make a request to the CC Realtime Metrics queues endpoint. ```bash curl --location --request GET 'https://api.8x8.com/analytics/cc/v5/realtime-metrics/queues' \ --header 'Authorization: Bearer 3yKcgVwWCJM14dXxKDBAEDGcythJ' ``` **api_product_list** : This is the list of APIs the provided credentials (and this generated access_token) are valid for. This can be one or more API Products. Adding or removing APIs from an existing API key will not be instantaneous as there is some replication delay for cached objects. ## List of APIs and whether they use this OAuth process | API Product Name | API Description | OAuth | | ---------------------- | ---------------------------------------------- | ----- | | analytics realtime-api | Contact Center Realtime & Historical Analytics | ✔️ | | storage | Cloud Storage Service | ✔️ | | QM - API | Quality Management & Speech Analytics | ✔️ | | vcc | Contact Center Chat | ✔️ | | Chat | CHAPI Work Chat | ❌ | | analytics product | Work Analytics | ❌ | | customer-360 | Customer 360 | ❌ | --- ## Ring Group Member Summary > 📘 Looking for live/real-time queue, agent or call-level data? See [Work Analytics — Overview](/analytics/docs/work-analytics-queue-agent-reports). > 📘 **Updated Endpoint** > > The [Ring Group Member Summary](/analytics/docs/ring-group-member-summary) and [Ring Group Summary](/analytics/docs/wa-ring-group-summary) are dedicated endpoints to replace the previous [Ring Group & Ring Group Member Summary](/analytics/docs/work-analytics-ring-group-summary) endpoint which served both purposes > > > 📘 > > Note: This API provides access to data from the past 2 years only in accordance with Analytics for Work data compliance policies; queries spanning more than 2 years will return only the most recent 2 years of data, and queries outside this range will return no results > > > 📘 **You will need a working API key to begin** > > You can generate API credentials from [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > The `8x8-api-key` will be the `Key` generated. For Work Analytics the Secret from Admin Console is not required. > > Use the following base URL during this process: * `https://api.8x8.com/analytics/work` ## Run Ring Group Member Summary This will return a summary each member of each Ring Group in the specified PBXs for the duration specified. > 📘 **Ring Group Member Summary Reference** > > You can check out [Ring Group Member Summary Reference](/analytics/docs/ring-group-member-summary) but you won't be able to try it yet. > > ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyyy.zzzzzzzzzzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version for ring group summary is v2 | v2 | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | Pass the pbxId (PBX Name) of the requested pbx or comma separated list of pbxIds or `allpbxes` for all of the pbxs in the customer account. PBX names can be found [here in Admin Console](https://admin.8x8.com/company/pbx) | acmecorp,acmecorp2 | | startTime | ✓ | The interval start time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 08:30:00 | | endTime | ✓ | The interval end time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 19:00:00 | | timeZone | ✓ | [IANA Time Zones](https://www.iana.org/time-zones). Examples America/New_York, Europe/London [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | America/New_York | | extId | ✓ | The extension number of the ring group. Only a single ring group extension can be specified. | 100169 | | collapse | ☐ | This true or false value determines whether to collapse ring group member summaries into a single summary, even if the user information has changed during the date range selected; otherwise a distinct summary will be created for each distinct set of member data. Default is true | true | ### Ring Group Member Summary Request As per the Open API specification guidelines, we have migrated the URLs for Ring Group Member Summary Report as give in the below table : | Deprecated URL Version | Migrated URL (Current version) | | --- | --- | | v2/rgsum?extId={extId} | v2/rgsum-members?extId={extId} | > 📘 **Try out the Ring Group Member Summary** > > How does average talk time compare across users? Lets find out @ [Ring Group Member Summary Reference](/analytics/docs/ring-group-member-summary) > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v{version}/rgsum-members?pbxId={pbxId here}&startTime=2022-01-03 00:00:00&endTime=2022-05-03 10:00:00&timeZone=America/New_York&extId={extId here}' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ### Ring Group Member Summary Response For details on the company summary metrics please refer to [Ring Group Member Summary Glossary](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/ring-group-summary.htm#Glossary) > 📘 **Durations are in milliseconds** > > ```json [ { "pbxId": "acpmecorp", "site": "West", "firstName": "Marty", "lastName": "McFly", "ringGroupName": "Management", "ringGroupExtension": "100169", "extension": "100065", "totalAnswered": 0, "totalAdvanced": 5, "totalTalkTime": 0, "totalRingTime": 53821, "totalCalls": 5, "avgRingTime": 10764, "avgTalkTime": 0, "offered": 5 }, { "pbxId": "acpmecorp", "site": "West", "firstName": "Jane", "lastName": "Li", "ringGroupName": "Managementz", "ringGroupExtension": "100169", "extension": "100066", "totalAnswered": 2, "totalAdvanced": 1, "totalTalkTime": 148789, "totalRingTime": 31380, "totalCalls": 3, "avgRingTime": 10460, "avgTalkTime": 74394, "offered": 3 } ] ``` --- ## Ring Group Summary > 📘 Looking for live/real-time queue, agent or call-level data? See [Work Analytics — Overview](/analytics/docs/work-analytics-queue-agent-reports). > 📘 **Updated Endpoint** > > The [Ring Group Member Summary](/analytics/docs/ring-group-member-summary) and [Ring Group Summary](/analytics/docs/wa-ring-group-summary) are dedicated endpoints to replace the previous [Ring Group & Ring Group Member Summary](/analytics/docs/work-analytics-ring-group-summary) endpoint which served both purposes > > > 📘 > > Note: This API provides access to data from the past 2 years only in accordance with Analytics for Work data compliance policies; queries spanning more than 2 years will return only the most recent 2 years of data, and queries outside this range will return no results > > > 📘 **You will need a working API key to begin** > > You can generate API credentials from [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > The `8x8-api-key` will be the `Key` generated. For Work Analytics the Secret from Admin Console is not required. > > Use the following base URL during this process: * `https://api.8x8.com/analytics/work` ## Run Ring Group Summary This will return a summary for all of the Ring Groups in the specified PBXs for the duration specified. ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyyy.zzzzzzzzzzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version for ring group summary is v2 | v2 | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | Pass the pbxId (PBX Name) of the requested pbx or comma separated list of pbxIds or `allpbxes` for all of the pbxs in the customer account. PBX names can be found [here in Admin Console](https://admin.8x8.com/company/pbx) | acmecorp,acmecorp2 | | startTime | ✓ | The interval start time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 08:30:00 | | endTime | ✓ | The interval end time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 19:00:00 | | timeZone | ✓ | [IANA Time Zones](https://www.iana.org/time-zones). Examples America/New_York, Europe/London [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | America/New_York | ### Ring Group Summary Request As per the Open API specification guidelines, we have migrated the URLs for Ring Group Summary Report as give in the below table : | Deprecated URL Version | Migrated URL (Current version) | | --- | --- | | v2/rgsum | v2/rgsum-groups | > 📘 **Try out the Ring Group Summary** > > Which group has the least missed calls, lets find out @ [Ring Group Summary Reference](/analytics/reference/ring-group-summary) > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v{version}/rgsum-groups?pbxId={pbxId here}&startTime=2022-02-03 00:00:00&endTime=2022-02-03 10:00:00&timeZone=America/New_York' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ### Ring Group Summary Response For details on the company summary metrics please refer to [Ring Group Summary Glossary](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/ring-group-summary.htm#Glossary) > 📘 **Durations are in milliseconds** > > ```json [ { "pbxId": "acmecorp", "site": "East", "name": "Marketing", "extension": "100027", "totalMembers": 0, "totalInbound": 1, "totalAbandoned": 1, "totalAnswered": 0, "totalMissed": 1, "totalCallsToVM": 0, "totalAdvanced": 0, "totalRgTime": 5475, "totalTalkTime": 0, "totalCalls": 1, "avgAbandonedTime": 5475, "avgRgTime": 5475, "avgRingTime": 283, "avgTalkTime": 0, "totalAbandonedTime": 5475, "totalRingTime": 283 }, { "pbxId": "acmecorp2", "site": "West", "name": "Sales", "extension": "100055", "totalMembers": 0, "totalInbound": 12, "totalAbandoned": 0, "totalAnswered": 0, "totalMissed": 12, "totalCallsToVM": 12, "totalAdvanced": 0, "totalRgTime": 0, "totalTalkTime": 0, "totalCalls": 12, "avgAbandonedTime": 0, "avgRgTime": 0, "avgRingTime": 0, "avgTalkTime": 0, "totalAbandonedTime": 0, "totalRingTime": 0 } ] ``` --- ## Active Calls The **Active Calls** view returns the calls that are **currently active (in progress)** for a PBX at the moment of the query — a real-time snapshot of who is on a call right now, with each call's participants, current state and running durations. Because it reports live state, this endpoint takes **no date range** — there is no `startDate`/`endDate`. It always returns the calls active at the instant the request is served. One endpoint backs this report: * `GET /v2/pbxes/{pbxId}/calls/active` — the active (in-progress) calls for a PBX. [Active Calls reference](/analytics/reference/get-active-calls) ## Request **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-apikey provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | The `access_token` as a Bearer token | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyy.zzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | The opaque id of the PBX (not the PBX name). | P2DlDO1HSKe0uPdZlClziw | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | paging | | Optional. Page controls — page number and size. | 0,50 | | sorting | | Optional. Sort field and direction. Sortable fields: `callId`, `startTime`, `caller.name`, `callee.name`. | startTime,desc | | fields | | Comma-separated response fields to return (see below). If omitted, all available fields are returned. | callId,startTime,direction,outcome | > 📘 **Selecting fields, not metrics** > > Unlike the aggregated calls endpoint (which takes a `metrics` parameter), Active Calls selects which **response fields** to return via the `fields` parameter. The full list of selectable fields and their descriptions is on the [reference page](/analytics/reference/get-active-calls). > > ### Fields Full descriptions are on the [reference page](/analytics/reference/get-active-calls). They fall into these groups: * **Identity & timing** — `callId`, `startTime`, `connectedTime`, `totalDuration` * **Participants** — `callerId`, `callerAddress`, `callerName`, `callerDeviceId`, `callerDeviceModel`, `latestCalleeAddress`, `latestCalleeName`, `latestCalleeDeviceId`, `latestCalleeDeviceModel` * **State & classification** — `direction`, `outcome`, `latestLabel`, `labels`, `reference`, `sip` * **Live durations** — `talkingDuration`, `onHoldDuration`, `waitingDuration` ### Example request > 📘 **Try it out** > > You can try this endpoint from the [reference](/analytics/reference/get-active-calls). > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v2/pbxes/{pbxId}/calls/active?paging=0,50&sorting=startTime,desc&fields=callId,startTime,direction,outcome' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ## Response Each entry in `calls` describes one currently-active call: its `callId`, the `caller` and `latestCallee` participants, the `direction` and `outcome`, the state `labels` it has passed through so far, and its running durations. > 📘 **Durations are in seconds; timestamps are epoch milliseconds.** > > ```json { "calls": [ { "callId": "1717751880000", "startTime": 1717751880000, "connectedTime": 1717751885000, "caller": { "id": "Chay Hoss,1006", "address": "1006", "name": "Chay Hoss", "deviceModel": "voo8x8" }, "latestCallee": { "address": "1014", "name": "Chandler Hollow", "deviceModel": "voo8x8" }, "direction": "INBOUND", "reference": "1000", "sip": "sip-1717751880000", "latestLabel": "TALKING", "labels": ["WAITING", "ALERTING", "TALKING"], "outcome": "ONGOING", "talkingDuration": 42.38, "onHoldDuration": 0, "waitingDuration": 12.5, "totalDuration": 54.88 } ] } ``` For the complete list of selectable fields and their descriptions, see the [Active Calls reference](/analytics/reference/get-active-calls). --- ## Agent Activity The **Agent Activity** report shows per-agent interaction handling across the PBX — how many interactions each agent accepted, missed or rejected, the time they spent talking, wrapping up and on hold, their current status, and any interaction they are handling right now. It backs the User Status view in Analytics for Work. It is served by a single endpoint: * `GET /v2/pbxes/{pbxId}/agent-activity` — [Agent Activity reference](/analytics/reference/get-agent-activity) ## Request **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-apikey provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | The `access_token` as a Bearer token | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyy.zzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | The opaque id of the PBX (not the PBX name). | P2DlDO1HSKe0uPdZlClziw | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | userIds | | Restrict to specific agents/users (comma-separated). | u1,u2 | | userStatuses | | Filter by current user status (comma-separated). | AVAILABLE,BUSY | | queueIds | | Restrict to agents in specific queues (comma-separated). | q1 | | siteIds | | Restrict to specific sites (comma-separated). | site1 | | startDate / endDate | | Historical window in ISO 8601. Omit both for the current live state. | 2026-07-01T00:00:00Z | | intraDayStart / intraDayEnd | | Optional intra-day window; only valid together with `startDate`/`endDate`. | 2026-07-01T09:00:00Z | | metrics | | Comma-separated metrics to return (see below). | acceptedInteractions,agentTotalTalkTime | | includeSummary | | Defaults to `true`; pass `false` to omit the aggregated summary of the metrics. | false | ### Metrics Full descriptions are on the [reference page](/analytics/reference/get-agent-activity). They fall into these groups: * **Volume** — `enteredInteractions`, `acceptedInteractions`, `acceptedInteractionsPercentage`, `missedInteractions`, `missedInteractionsPercentage`, `rejectedInteractions`, `rejectedInteractionsPercentage` * **Timing** (seconds) — `agentAverageTimeToAnswer`, `agentTotalTalkTime`, `averageTalkingInteractionsTime`, `averageOnHoldInteractionsTime`, `averageWrapUpTime`, `totalInteractionTime` * **Status** — `userStatusTime` * **Live (ongoing)** — `ongoingHandlingInteractions`, `ongoingTalkingInteractions`, `ongoingOnHoldInteractions`, `ongoingWrapUpInteractions`, `ongoingAvgTalkingInteractionsTime`, `ongoingAvgHandlingInteractionsTime`, `ongoingTalkingInteractionsTime`, `ongoingOnHoldInteractionsTime`, `ongoingWrapUpInteractionsTime`, `ongoingTotalHandlingInteractionsTime` ### Example request > 📘 **Try it out** > > You can try this endpoint from the [reference](/analytics/reference/get-agent-activity). > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v2/pbxes/{pbxId}/agent-activity?startDate=2026-07-01T00:00:00Z&endDate=2026-07-15T23:59:59Z&metrics=acceptedInteractions,missedInteractions,agentTotalTalkTime' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ## Response Each entry in `agentActivity` identifies the agent (`userId`, `agentExtension`, `agentName`), their `userStatus` / `activityStatus`, the queues they are logged in to, any `interaction` they are currently handling, and the requested `metrics`. When `includeSummary=true`, a top-level `summary` aggregates the metrics. > 📘 **Durations are in seconds.** Timing and percentage values may be decimals (percentages are 0–100); only counts are integers. > > ```json { "agentActivity": [ { "userId": "5f2c1a90-1b23-4d56-8e90-abcdef012345", "agentExtension": "1014", "agentName": "Chandler Hollow", "userStatus": "AVAILABLE", "activityStatus": "HANDLING", "loggedInQueues": ["Support CQ", "Sales CQ"], "interaction": { "caller": "Chay Hoss", "address": "1006" }, "metrics": { "acceptedInteractions": 42, "missedInteractions": 3, "rejectedInteractions": 1, "agentTotalTalkTime": 5820.5, "averageWrapUpTime": 12.4, "userStatusTime": 3600.0, "ongoingTalkingInteractions": 1 } } ], "summary": { "acceptedInteractions": 42, "missedInteractions": 3 } } ``` ### Response fields | Field | Description | | --- | --- | | agentActivity[] | One entry per agent matching the filters | | agentActivity[].userId / agentExtension / agentName | Agent identifier, extension and display name | | agentActivity[].userStatus / activityStatus | The agent's presence status and current call-handling activity | | agentActivity[].loggedInQueues | Queues the agent is currently logged in to | | agentActivity[].interaction | The interaction the agent is currently handling (`caller`, `address`), if any | | agentActivity[].metrics | Object holding each requested metric and its value | | summary | Aggregated metrics across all returned agents (only when `includeSummary=true`) | --- ## Aggregated & Detailed Calls The **Calls** views return call-level analytics for a PBX — an aggregated, paged table of calls with their participants, outcome and handling durations, and a detailed drill-down for a single call. Two endpoints back this report: * `GET /v2/pbxes/{pbxId}/calls` — aggregated, paged call table. [Call Queue Data Table reference](/analytics/reference/aggregated-3) * `GET /v2/pbxes/{pbxId}/calls/{callId}` — detail for one call. [Call Details reference](/analytics/reference/detailed) ## Request **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-apikey provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | The `access_token` as a Bearer token | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyy.zzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | The opaque id of the PBX (not the PBX name). | P2DlDO1HSKe0uPdZlClziw | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | paging | | Optional. Page size via `limit`; page forward with the `startingAfter` cursor. | 50 | | sorting | | Optional. Use `sortingField` + `sortingDirection` (`ASC`/`DESC`). | sortingField=startTime&sortingDirection=DESC | | queueIds | | Restrict to specific queues — comma-separated **opaque queue ids**. | JMwyX0BUTRC9uAnuAz8wfg | | siteIds | | Restrict to specific sites — comma-separated **opaque site ids**. | vcXZjfBOT2yWxhUQHzwqwe | | startDate / endDate | | Historical window in ISO 8601. Omit both for the current live state. | 2026-07-01T00:00:00Z | | intraDayStart / intraDayEnd | | Optional intra-day window; only valid together with `startDate`/`endDate`. | 2026-07-01T09:00:00Z | > 📘 **Pagination is cursor-based** > > This endpoint paginates by cursor, not page number: set `limit` for the page size and pass the previous page's last `callId` as `startingAfter` (with `latestSortingValue`) to fetch the next page. Sort with `sortingField` + `sortingDirection`. Both are optional. > > ### Example request > 📘 **Try it out** > > You can try this endpoint from the [reference](/analytics/reference/aggregated-3). > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v2/pbxes/{pbxId}/calls?startDate=2026-07-01T00:00:00Z&endDate=2026-07-15T23:59:59Z&limit=50&sortingField=startTime&sortingDirection=DESC' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ## Response Each entry in `calls` describes one call: its `callId`, the `workGroup` (queue or ring group) it belonged to, the `caller` and `latestCallee` participants, the lifecycle `latestLabel` / `outcome` / `resolution`, the `labels` it passed through, and its durations. > 📘 **Durations are in seconds; timestamps are epoch milliseconds.** > > ```json { "calls": [ { "callId": "1715191662606", "workGroup": { "id": "tqteLkZOScyi0Mai9ewUlA", "extension": "1000", "groupType": "CALL_QUEUE" }, "caller": { "id": "Caller,1006", "address": "1006", "name": "Chay Hoss", "deviceModel": "voo8x8" }, "latestCallee": { "address": "1014", "name": "Chandler Hollow", "deviceModel": "voo8x8" }, "latestLabel": "FINISHED", "outcome": "FINISHED", "labels": ["WAITING", "ALERTING", "MISSED", "DISCONNECTED", "FINISHED"], "startTime": 1717751880000, "stopTime": 1717751880000, "connectedTime": 0, "handlingDuration": 0, "waitingDuration": 42.38, "talkingDuration": 0, "onHoldDuration": 0, "totalDuration": 42.38, "did": "1000", "direction": "INTERNAL", "resolution": "OFFER_TIMEOUT" } ] } ``` ### Response fields | Field | Description | | --- | --- | | calls[] | One entry per call in the current page | | calls[].callId | Unique call identifier — pass it to `GET .../calls/{callId}` for full detail | | calls[].workGroup | The queue or ring group involved (`id`, `extension`, `groupType`) | | calls[].caller / latestCallee | The originating party and the most recent party handled the call | | calls[].latestLabel / outcome / resolution | Most recent state label (last element of `labels`), final outcome, and resolution classification | | calls[].labels | Ordered list of states the call passed through | | calls[].handlingDuration / waitingDuration / talkingDuration / onHoldDuration / totalDuration | Per-phase and total call durations (seconds) | | calls[].startTime / stopTime / connectedTime | Call timestamps (epoch ms) | | calls[].did | The dialled number (DID) the call came in on | | calls[].direction | INTERNAL / INBOUND / OUTBOUND | ### Call detail To retrieve the full detail (legs, participants and timeline) of a single call, call `GET /v2/pbxes/{pbxId}/calls/{callId}` with the `callId` from the aggregated response. This endpoint also **requires `startDate`/`endDate`** (the same window the call falls in). Each leg carries a `callee` object and a `statusDuration` (in seconds). See the [Call Details reference](/analytics/reference/detailed). --- ## Call Detail Records > 📘 Looking for live/real-time queue, agent or call-level data? See [Work Analytics — Overview](/analytics/docs/work-analytics-queue-agent-reports). > 📘 **Updated Endpoint** > > The [Call Legs](/analytics/docs/work-analytics-call-legs) and [Call Detail Records](/analytics/docs/work-analytics-call-detail-records) are dedicated endpoints to replace the previous [Call Detail Record Legs](/analytics/docs/work-analytics-cdr-report) endpoint which served both purposes > > > 📘 > > Note: This API provides access to data from the past 2 years only in accordance with Analytics for Work data compliance policies; queries spanning more than 2 years will return only the most recent 2 years of data, and queries outside this range will return no results > > ## Call Records Explained A Call Record is a single record view of the overall call and metrics represented by a single Call ID. Example: A Call Record would be a single row representation of a call that follows the following path which would have multiple Call Legs to fully represent the journey. * Inbound to an Auto Attendant * Transferred to Ring Group * Simultaneous calls to 5 Ring Group members (Leg per member contacted regardless of outcome) * Answered by one of the members [8x8 Work Analytics Historical](/analytics/reference/authentication-1) access is via this multi step process. For any of the endpoints the same process is followed. > 📘 **You will need a working API key to begin** > > You can generate API credentials from [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > The `8x8-api-key` will be the `Key` generated. For Work Analytics the Secret from Admin Console is not required. > > Use the following base URL during this process: * `https://api.8x8.com/analytics/work` ## Run Report ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | |---------------|----------|-------------------------------------------------------------------------------------------------------------|-------------------------------------------------| | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyy.zzzzzzzzzzz | #### Path | Name | Required | Description | Example | |---------|----------|---------------------------------------------|---------| | version | ✓ | The current version for /call-records is v2 | v2 | #### Query | Name | Required | Description | Example | |-----------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------| | pbxId | ✓ | Pass the pbxId (PBX Name) of the requested pbx or comma separated list of pbxIds or `allpbxes` for all of the pbxs in the customer account. PBX names can be found [here in Admin Console](https://admin.8x8.com/company/pbx) | `acmecorp,acmecorp2` | | startTime | ✓ | The interval start time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 08:30:00 | | endTime | ✓ | The interval end time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 19:00:00 | | timeZone | ✓ | [IANA Time Zones](https://www.iana.org/time-zones). Examples America/New_York, Europe/London [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | America/New_York | | pageSize | ✓ | Number of records to return in pages See [Pagination](/analytics/docs/work-analytics-cdr-report#pagination). Must be | 50 | | scrollId | ☐/✓ | Not required for initial page required on subsequent pages. See [Pagination](/analytics/docs/work-analytics-cdr-report#pagination) | | ### Call Records Request As per the Open API specification guidelines, we have migrated the URLs for Call Record Report as give in the below table | Deprecated URL Version | Migrated URL (Current version) | |---------------------------|--------------------------------| | v1/cdr?isCallRecord= true | v2/call-records | > 📘 **Try out the CDR Records Request** > > You can check out [CDR Records Reference](/analytics/reference/call-detail-records) but currently this one can't be tested from the Reference. > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v{version}/call-records?pbxId={{pbxId here}}&startTime=2022-02-03 00:00:00&endTime=2022-02-03 10:00:00&timeZone=America/New_York&pageSize=50' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ### Call Records Response For details on call-records metrics please refer to[CDR Glossary and Details](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/call-detail-record.htm) ```json { "meta": { "totalRecordCount": 2, "scrollId": "c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODM0NV8xNjQzODk3NDkyODk3" }, "data": [ { "dnis": "12025555720", "aaDestination": null, "callId": "1635571048351", "startTimeUTC": 1643898447493, "startTime": "2022-02-03T09:27:27.493-0500", "connectTimeUTC": 0, "connectTime": "0", "disconnectedTimeUTC": 1643898472253, "disconnectedTime": "2022-02-03T09:27:52.253-0500", "talkTimeMS": 0, "talkTime": "00:00:00", "caller": "+15555551220", "callerName": "MARK SMITH", "callee": "120088", "calleeName": "Jane Li", "direction": "Incoming", "callerId": "MARK SMITH,+15555551220", "missed": "Missed", "abandoned": "-", "answered": "-", "answeredTime": 0, "calleeDisconnectOnHold": "", "callerDisconnectOnHold": "", "pbxId": "acmecorppbx", "sipCallId": "user@example.com", "lastLegDisposition": "Voicemail", "callLegCount": "1", "callTime": 24760, "ringDuration": 39, "abandonedTime": 0, "calleeHoldDurationMS": 0, "calleeHoldDuration": "00:00:00", "waitTimeMS": 0, "waitTime": "00:00:00", "departments": [ "Sales Engineering" ], "branches": [ "Central" ] }, { "dnis": "15554441212", "aaDestination": null, "callId": "1635571048345", "startTimeUTC": 1643897492897, "startTime": "2022-02-03T09:11:32.897-0500", "connectTimeUTC": 0, "connectTime": "0", "disconnectedTimeUTC": 1643899189421, "disconnectedTime": "2022-02-03T09:39:49.421-0500", "talkTimeMS": 0, "talkTime": "00:00:00", "caller": "+15551234567", "callerName": "15551234567", "callee": "CallQueue", "calleeName": "Test Queue", "direction": "Incoming", "callerId": "15551234567,+15551234567", "missed": "Missed", "abandoned": "Abandoned", "answered": "-", "answeredTime": 0, "calleeDisconnectOnHold": "", "callerDisconnectOnHold": "", "pbxId": "acmecorppbx", "sipCallId": "user@example.com", "lastLegDisposition": "Missed", "callLegCount": "1", "callTime": 1696524, "ringDuration": 197, "abandonedTime": 1696524, "calleeHoldDurationMS": 0, "calleeHoldDuration": "00:00:00", "waitTimeMS": 1696320, "waitTime": "00:28:16", "departments": [ "East Office" ], "branches": [ "East Coast" ] } ] } ``` > 👍 **Follow the pagination steps below to retrieve subsequent pages.** > > #### Pagination Within Work Analytics Only the /call-records and /call-legs endpoints are subject to pagination. This is controlled by `pageSize` and `scrollId` * `pageSize` is the number of records to return per page and is required for /call-records * `scrollId` is returned from /call-records requests providing an id for the next page of results. **Pagination Example** Assuming there will be 81 records in total. With an initial input of `pageSize=50` the returned meta data will be as follows. Note: data has been truncated to an empty array to limit the size of the example text ```json { "meta": { "totalRecordCount": 81, "scrollId": "c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODQ4Nl8xXzE2NDM5MTIzNTI0NDE" }, "data": [ ] } ``` The request for the next page would include scrollId set as the value returned in the previous request `pageKey=50&scrollId=c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODQ4Nl8xXzE2NDM5MTIzNTI0NDE` The new result set would look as follows. The returned result set would only have 31 elements. ```json { "meta": { "totalRecordCount": 81, "scrollId": "c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODM0NV8xXzE2NDM4OTc0OTI5MDM" }, "data": [ ] } ``` The request for the next page would be `pageKey=50&scrollId=c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODM0NV8xXzE2NDM4OTc0OTI5MDM` the scrollId has been set to the value returned in the previous request ```json { "meta": { "totalRecordCount": 0, "scrollId": "No Data" }, "data": [] } ``` --- ## Call Legs > 📘 Looking for live/real-time queue, agent or call-level data? See [Work Analytics — Overview](/analytics/docs/work-analytics-queue-agent-reports). > 📘 **Updated Endpoint** > > The [Call Legs](/analytics/docs/work-analytics-call-legs) and [Call Detail Records](/analytics/docs/work-analytics-call-detail-records) are dedicated endpoints to replace the previous [Call Detail Record Legs](/analytics/docs/work-analytics-cdr-report) endpoint which served both purposes > > > 📘 > > Note: This API provides access to data from the past 2 years only in accordance with Analytics for Work data compliance policies; queries spanning more than 2 years will return only the most recent 2 years of data, and queries outside this range will return no results > > ## Call Legs Explained A Call Record is a single record view of the overall call and metrics represented by a single Call ID. A Call Leg provides detailed metrics on an individual segment of a call within the call journey and is represented by a Call Leg ID combined with a Call ID. Example: A Call Record would be a single row representation of a call that follows the following path which would have multiple Call Legs to fully represent the journey. * Inbound to an Auto Attendant * Transferred to Ring Group * Simultaneous calls to 5 Ring Group members (Leg per member contacted regardless of outcome) * Answered by one of the members [8x8 Work Analytics Historical](/analytics/reference/authentication-1) access is via this multi step process. For any of the endpoints the same process is followed. > 📘 **You will need a working API key to begin** > > You can generate API credentials from [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > The `8x8-api-key` will be the `Key` generated. For Work Analytics the Secret from Admin Console is not required. > > Use the following base URL during this process: * `https://api.8x8.com/analytics/work` ## Run Report ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | |---------------|----------|-------------------------------------------------------------------------------------------------------------|-------------------------------------------------| | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyy.zzzzzzzzzzz | #### Path | Name | Required | Description | Example | |---------|----------|-----------------------------------|---------| | version | ✓ | The current version for cdr is v1 | v1 | #### Query | Name | Required | Description | Example | |---------------------------------------------------------------------------------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------| | pbxId | ✓ | Pass the pbxId (PBX Name) of the requested pbx or comma separated list of pbxIds or `allpbxes` for all of the pbxs in the customer account. PBX names can be found [here in Admin Console](https://admin.8x8.com/company/pbx) | `acmecorp,acmecorp2` | | startTime | ✓ | The interval start time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 08:30:00 | | endTime | ✓ | The interval end time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 19:00:00 | | timeZone | ✓ | [IANA Time Zones](https://www.iana.org/time-zones). Examples America/New_York, Europe/London [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | America/New_York | | pageSize | ✓ | Number of records to return in pages See [Pagination](/analytics/docs/work-analytics-cdr-report#pagination). Must be | 50 | | scrollId | ☐/✓ | Not required for initial page required on subsequent pages. See [Pagination](/analytics/docs/work-analytics-cdr-report#pagination) | | | isConnectTime | ☐ | This parameter allows you to get your results based on either connect time or disconnect time of each call leg. Default is false. See [isConnectTime explained](/analytics/docs/work-analytics-cdr-report#isconnecttime-explained) for more details. | false | | isSimplified | ☐ | Only valid for Call Legs. Default is false. When true: Child calls are not returned. For example, child calls are the ones the service makes to one or more agents when a call comes into a call queue or a ring group. Some call legs are omitted. For example, the removal of CallForking and CallRecording legs. | false | #### isConnectTime explained This looks at the timing of call legs and as such is not valid for call records if `false` will use 'disconnected_time' of the call leg to filter data and return the result if `true` will use the 'connected_time' of the call leg to filter data and return the result **disconnected_time** is when the **call leg** was disconnected from the system **connected_time** is when the **call leg** transitioned from alerting to connected. ### Call Legs Request As per the Open API specification guidelines, we have migrated the URLs for Call Leg Report as give in the below table : | Deprecated URL Version | Migrated URL (Current version) | |---------------------------|--------------------------------| | v1/cdr?isCallRecord=false | v2/call-legs | Try out the CDR Legs Request > 📘 **Jump in and try it out in the [CDR Legs Reference](/analytics/reference/call-detail-record-legs)** > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v{version}/call-legs?pbxId={pbxId here}&startTime=2022-02-03 00:00:00&endTime=2022-02-03 10:00:00&timeZone=America/New_York&pageSize=50&isSimplified=false&isConnectTime=false' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ### Call Legs Response For details on call legs metrics please refer to[CDR Glossary and Details](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/call-detail-record.htm) ```json { "meta": { "totalRecordCount": 2, "scrollId": "c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODM0NV8xXzE2NDM4OTc0OTI5MDM" }, "data": [ { "callId": "1635571048351", "legId": "1", "startTimeUTC": 1643898447499, "startTime": "2022-02-03T09:27:27.499-0500", "connectTimeUTC": 1643898447539, "connectTime": "2022-02-03T09:27:27.539-0500", "disconnectedTimeUTC": 1643898472253, "disconnectedTime": "2022-02-03T09:27:52.253-0500", "talkTimeMS": 0, "talkTime": "00:00:00", "caller": "+15555551220", "callerName": "MARK SMITH", "callee": "120088", "calleeName": "Jane Li", "lra": "120088", "direction": "Incoming", "parentCallId": null, "transferToCallId": null, "dnis": "12025555720", "status": "Completed", "callerDeviceId": null, "calleeDeviceId": null, "callerDeviceModel": "", "calleeDeviceModel": "", "callerId": "MARK SMITH,+15555551220", "missed": "Missed", "abandoned": "-", "answered": "-", "cause": "Ring No Answer", "callerSvcName": null, "callerSvcType": null, "calleeSvcName": "VMadvanced", "calleeSvcType": "Custom", "lraType": 1, "calleeHoldDurationMS": 0, "calleeHoldDuration": "00:00:00", "calleeDisconnectOnHold": "", "callerDisconnectOnHold": "", "pbxId": "acmecorppbx", "sipCallId": "user@example.com", "departments": [ "Sales Engineering" ], "branches": [ "Central" ], "recordServiceOn": "", "bargeServiceOn": "", "masterSlaveExts": "", "propsLastPartyDisp": "Voicemail", "accountCode": "", "aaPath": "", "callTime": 24754 }, { "callId": "1635571048345", "legId": "1", "startTimeUTC": 1643897492903, "startTime": "2022-02-03T09:11:32.903-0500", "connectTimeUTC": 1643897493100, "connectTime": "2022-02-03T09:11:33.100-0500", "disconnectedTimeUTC": 1643899189420, "disconnectedTime": "2022-02-03T09:39:49.420-0500", "talkTimeMS": 0, "talkTime": "00:00:00", "caller": "+15551234567", "callerName": "15551234567", "callee": "CallQueue", "calleeName": "Test Queue", "lra": "110081", "direction": "Incoming", "parentCallId": null, "transferToCallId": null, "dnis": "15554441212", "status": "Completed", "callerDeviceId": null, "calleeDeviceId": null, "callerDeviceModel": "", "calleeDeviceModel": "", "callerId": "14164666541,+14164666541", "missed": "Missed", "abandoned": "Abandoned", "answered": "-", "cause": "Normal", "callerSvcName": null, "callerSvcType": null, "calleeSvcName": "ACDOperatorService", "calleeSvcType": "Custom", "lraType": 4, "calleeHoldDurationMS": 0, "calleeHoldDuration": "00:00:00", "calleeDisconnectOnHold": "", "callerDisconnectOnHold": "", "pbxId": "acmecorppbx", "sipCallId": "user@example.com", "departments": [ "East Office" ], "branches": [ "East Coast" ], "recordServiceOn": "", "bargeServiceOn": "", "masterSlaveExts": "", "propsLastPartyDisp": "Missed", "accountCode": "", "aaPath": "", "callTime": 1696517 } ] } ``` > 👍 **Follow the pagination steps below to retrieve subsequent pages.** > > #### Pagination Within Work Analytics Only the /call-records and /call-legs endpoints are subject to pagination. This is controlled by `pageSize` and `scrollId` * `pageSize` is the number of records to return per page and is required for /call-legs * `scrollId` is returned from /call-legs requests providing an id for the next page of results. **Pagination Example** Assuming there will be 81 records in total. With an initial input of `pageSize=50` the returned meta data will be as follows. Note: data has been truncated to an empty array to limit the size of the example text ```json { "meta": { "totalRecordCount": 81, "scrollId": "c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODQ4Nl8xXzE2NDM5MTIzNTI0NDE" }, "data": [ ] } ``` The request for the next page would include scrollId set as the value returned in the previous request `pageKey=50&scrollId=c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODQ4Nl8xXzE2NDM5MTIzNTI0NDE` The new result set would look as follows. The returned result set would only have 31 elements. ```json { "meta": { "totalRecordCount": 81, "scrollId": "c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODM0NV8xXzE2NDM4OTc0OTI5MDM" }, "data": [ ] } ``` The request for the next page would be `pageKey=50&scrollId=c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODM0NV8xXzE2NDM4OTc0OTI5MDM` the scrollId has been set to the value returned in the previous request ```json { "meta": { "totalRecordCount": 0, "scrollId": "No Data" }, "data": [] } ``` --- ## Call Queues The **Call Queues** report shows how each call queue is performing — how many interactions entered, were accepted, abandoned or diverted, the timing of those interactions, and the current live state of the queue (waiting, on-hold and talking calls, and agent availability). It is served by a single endpoint: * `GET /v2/pbxes/{pbxId}/call-queue-metrics` — [Get Call Queue Metrics reference](/analytics/reference/get-queue-metrics) ## Request **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-apikey provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | The `access_token` as a Bearer token | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyy.zzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | The opaque id of the PBX (not the PBX name). | P2DlDO1HSKe0uPdZlClziw | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | queueIds | | Restrict to specific call queues (comma-separated). All queues if omitted. | q1,q2 | | siteIds | | Restrict to specific sites (comma-separated). All sites if omitted. | site1 | | startDate / endDate | | Historical window in ISO 8601. Omit both for the current live state. | 2026-07-01T00:00:00Z | | intraDayStart / intraDayEnd | | Optional intra-day window; only valid together with `startDate`/`endDate`. | 2026-07-01T09:00:00Z | | metrics | | Comma-separated metrics to return (see below). | enteredCalls,acceptedCalls,divertedCalls | | includeSummary | | Defaults to `true`; pass `false` to omit the aggregated summary across the selected queues. | false | ### Metrics The full list and per-metric descriptions are on the [reference page](/analytics/reference/get-queue-metrics). They fall into these groups: * **Volume** — `enteredCalls`, `acceptedCalls`, `acceptedCallsPercentage`, `completedCalls`, `missedCalls`, `missedCallsPercentage`, `abandonedInWaitingCalls`, `abandonedInWaitingCallsPercentage`, `abandonedInHandlingCalls`, `abandonedInHandlingCallsPercentage`, `divertedCalls`, `divertedCallsPercentage`, `forwardedToVoiceMail`, `activeCalls` * **Timing** (seconds) — `averageTimeToAnswer`, `averageHandlingTime`, `averageWaitingTime`, `averageOnHoldTime`, `averageTalkTime`, `longestWaitingTime`, `longestHandlingTime`, `longestOnHoldTime`, `longestTalkTime`, `totalHandlingTime`, `totalWaitingTime`, `totalTalkTime`, `totalOnHoldTime` * **Transfers** — `transferredToCallQueue`, `transferredToCallQueuePercentage`, `transferredToRingGroup`, `transferredToRingGroupPercentage`, `transferredToExternalNumber` * **Live (ongoing)** — `ongoingWaitingCalls`, `ongoingOnHoldCalls`, `ongoingTalkingCalls`, `ongoingTotalCalls`, `ongoingHandlingCalls`, `ongoingAvailableAgents`, `ongoingEligibleAgents`, `ongoingTotalAgents`, `ongoingEnabledPrimaryAgents`, `ongoingEnabledOverflowAgents`, `ongoingEligiblePrimaryAgents`, `ongoingEligibleOverflowAgents`, `ongoingOverflowAgents`, `ongoingPrimaryWrappingUpAgents`, `ongoingOverflowWrappingUpAgents`, `ongoingWrappingUpAgents`, `ongoingLoggedOutAndDndAgents`, `ongoingLoggedOutAndDndOverflowAgents`, `ongoingAvgHandlingTime`, `ongoingAvgTalkingTime`, `ongoingAvgWaitingTime`, `ongoingTotalHandlingTime`, `ongoingTalkingTime`, `ongoingOnHoldTime`, `ongoingWaitingTime`, `ongoingLongestHandlingTime`, `ongoingLongestOnHoldTime`, `ongoingLongestTalkingTime`, `ongoingAvgOnHoldTime`, `ongoingLongestWaitingTime` > 📘 **How the volume buckets relate** > > Every completed queue entry falls into exactly one outcome: `enteredCalls = acceptedCalls + abandonedInWaitingCalls + divertedCalls + missedCalls + abandonedInHandlingCalls`. `forwardedToVoiceMail` is a **sub-case of `divertedCalls`** — the system routed the caller to voicemail (or another queue, ring group or external number) before any agent was offered the call. `transferredTo*` are agent-initiated and can happen after answer, so they are counted within `acceptedCalls`, not as a separate bucket. > > ### Example request > 📘 **Try it out** > > You can try this endpoint from the [reference](/analytics/reference/get-queue-metrics). > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v2/pbxes/{pbxId}/call-queue-metrics?startDate=2026-07-01T00:00:00Z&endDate=2026-07-15T23:59:59Z&metrics=enteredCalls,acceptedCalls,divertedCalls,averageTimeToAnswer&includeSummary=true' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ## Response Each entry in `callQueues` identifies the queue (`pbx`, `site`, `id`, `name`, `extension`, and a `deleted` flag) and carries the requested metrics in a `metrics` object. Unless `includeSummary=false`, a top-level `metricsSummary` object aggregates the same metrics across all returned queues. > 📘 **Durations are in seconds.** > > ```json { "callQueues": [ { "pbx": "acmecorp", "site": "SanJose", "id": "tqteLkZOScyi0Mai9ewUlA", "name": "Support CQ", "deleted": false, "extension": "1000", "metrics": { "enteredCalls": 128, "acceptedCalls": 111, "acceptedCallsPercentage": 86.7, "abandonedInWaitingCalls": 12, "divertedCalls": 5, "divertedCallsPercentage": 3.9, "averageTimeToAnswer": 8.2, "averageHandlingTime": 254, "ongoingWaitingCalls": 2, "ongoingTalkingCalls": 6, "ongoingAvailableAgents": 4, "ongoingAvgOnHoldTime": 15 } } ], "metricsSummary": { "enteredCalls": 128, "acceptedCalls": 111, "divertedCalls": 5, "completedTotalsSum": 116 } } ``` ### Response fields | Field | Description | | --- | --- | | callQueues[] | One entry per call queue matching the filters | | callQueues[].id / name / extension | Queue identifier, display name and extension (a **string**) | | callQueues[].deleted | `true` if the queue has since been deleted | | callQueues[].pbx / site | PBX and site the queue belongs to | | callQueues[].metrics | Object holding each requested metric and its value | | metricsSummary | Aggregated metrics across all returned queues (present by default; omitted when `includeSummary=false`). Includes `completedTotalsSum`. | --- ## Call Detail Record and Call Legs > 🚧 **Updated Endpoints Available** > > The [Call Legs](/analytics/docs/work-analytics-call-legs) and [Call Detail Records](/analytics/docs/work-analytics-call-detail-records) are dedicated endpoints to replace the previous [Call Detail Record Legs](/analytics/docs/work-analytics-cdr-report) endpoint which served both purposes > > > 📘 > > Note: This API provides access to data from the past 2 years only in accordance with Analytics for Work data compliance policies; queries spanning more than 2 years will return only the most recent 2 years of data, and queries outside this range will return no results > > ## Call Records and Call Legs Explained A Call Record is a single record view of the overall call and metrics represented by a single Call ID. A Call Leg provides detailed metrics on an individual segment of a call within the call journey and is represented by a Call Leg ID combined with a Call ID. Example: A Call Record would be a single row representation of a call that follows the following path which would have multiple Call Legs to fully represent the journey. * Inbound to an Auto Attendant * Transferred to Ring Group * Simultaneous calls to 5 Ring Group members (Leg per member contacted regardless of outcome) * Answered by one of the members [8x8 Work Analytics Historical](/analytics/reference/authentication-1) access is via this multi step process. For any of the endpoints the same process is followed. > 📘 **You will need a working API key to begin** > > You can generate API credentials from [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > The `8x8-api-key` will be the `Key` generated. For Work Analytics the Secret from Admin Console is not required. > > Use the following base URL during this process: * `https://api.8x8.com/analytics/work` ## 1. Authenticate to retrieve access token You will use your API key combined with the user credentials of a user with permission and access to Work Analytics to authenticate, this user **does not need to be** the one who generated the API credentials > 🚧 **User must access Analytics at least once via browser** > > The users credentials will not be able to leverage the API until they have used Work Analytics via browser at least once > > ### Parameters **Method: POST** #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-api key provided | test_key_kjdfidj238jf9123df221 | | Content-Type | ✓ | Set content type to form-urlencoded | application/x-www-form-urlencoded | #### Body | Name | Required | Description | Example | |----------|----------|--------------------------------------------------------------------|-----------------------------------------------------| | username | ✓ | The 8x8 username of a user with Work Analytics access privileges | [someuser@acme.fakeco](mailto:someuser@acme.fakeco) | | password | ✓ | The 8x8 password of the user with Work Analytics access privileges | Rrnp5QBW6dTbx^TP | ### Authentication Request ```bash curl --location --request POST 'https://api.8x8.com/analytics/work/v1/oauth/token' \ --header '8x8-apikey: {8x8-apikey input here}' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'username={8x8 username of user input here}' \ --data-urlencode 'password={8x8 password of user input here}' ``` ### Authentication Response **Response** ```json { "access_token": "eyJhbGciOiJSUzI1NiJ9.yyyyyyy.zzzzzzzzzzz", "token_type": "bearer", "expires_in": 1800 } ``` **Outputs For Next Step:** * access_token * expires_in The token will expire in the number of seconds specified in expires_in. The following steps will use the access_token as a Bearer Token form of authentication. This takes the form of the `Authorization` header being set to `Bearer access_token` (Space between Bearer and the access_token) ## 2. Run Report ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | |---------------|----------|-------------------------------------------------------------------------------------------------------------|-------------------------------------------------| | 8x8-apikey | ✓ | The 8x8-api key provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyy.zzzzzzzzzzz | #### Path | Name | Required | Description | Example | |---------|----------|-----------------------------------|---------| | version | ✓ | The current version for cdr is v1 | v1 | #### Query | Name | Required | Description | Example | |---------------------------------------------------------------------------------------------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------| | pbxId | ✓ | Pass the pbxId (PBX Name) of the requested pbx or comma separated list of pbxIds or `allpbxes` for all of the pbxs in the customer account | `acmecorp,acmecorp2` | | startTime | ✓ | The interval start time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 08:30:00 | | endTime | ✓ | The interval end time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 19:00:00 | | timeZone | ✓ | [IANA Time Zones](https://www.iana.org/time-zones). Examples America/New_York, Europe/London [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | America/New_York | | pageSize | ✓ | Number of records to return in pages See [Pagination](/analytics/docs/work-analytics-cdr-report#pagination). Must be | 50 | | scrollId | ☐/✓ | Not required for initial page required on subsequent pages. See [Pagination](/analytics/docs/work-analytics-cdr-report#pagination) | | | isCallRecord | ☐ | When true return call records (single row per call, when false return call legs). Default is false | false | | isConnectTime | ☐ | This parameter allows you to get your results based on either connect time or disconnect time of each call leg. It is not compatible with isCallRecord. Default is false. See [isConnectTime explained](/analytics/docs/work-analytics-cdr-report#isconnecttime-explained) for more details. | false | | isSimplified | ☐ | Only valid for Call Legs (isCallRecord = false).Default is false. When true: Child calls are not returned. For example, child calls are the ones the service makes to one or more agents when a call comes into a call queue or a ring group. Some call legs are omitted. For example, the removal of CallForking and CallRecording legs. | false | #### isConnectTime explained This looks at the timing of call legs and as such is not valid when isCallRecord is `true` if `false` will use 'disconnected_time' of the call leg to filter data and return the result if `true` will use the 'connected_time' of the call leg to filter data and return the result **disconnected_time** is when the **call leg** was disconnected from the system **connected_time** is when the **call leg** transitioned from alerting to connected. ### Call Records Request > 📘 **Try out the CDR Records Request** > > You can check out [CDR Records Reference](/analytics/reference/call-detail-records) but currently this one can't be tested from the Reference. > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v{version}/cdr?pbxId={pbxId here}&startTime=2022-02-03 00:00:00&endTime=2022-02-03 10:00:00&timeZone=America/New_York&pageSize=50&isCallRecord=true&isSimplified=false&isConnectTime=false' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ### Call Records Response This sample response is for isCallRecord=true For details on cdr metrics please refer to[CDR Glossary and Details](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/call-detail-record.htm) ```json { "meta": { "totalRecordCount": 2, "scrollId": "c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODM0NV8xNjQzODk3NDkyODk3" }, "data": [ { "dnis": "12025555720", "aaDestination": null, "callId": "1635571048351", "startTimeUTC": 1643898447493, "startTime": "2022-02-03T09:27:27.493-0500", "connectTimeUTC": 0, "connectTime": "0", "disconnectedTimeUTC": 1643898472253, "disconnectedTime": "2022-02-03T09:27:52.253-0500", "talkTimeMS": 0, "talkTime": "00:00:00", "caller": "+15555551220", "callerName": "MARK SMITH", "callee": "120088", "calleeName": "Jane Li", "direction": "Incoming", "callerId": "MARK SMITH,+15555551220", "missed": "Missed", "abandoned": "-", "answered": "-", "answeredTime": 0, "calleeDisconnectOnHold": "", "callerDisconnectOnHold": "", "pbxId": "acmecorppbx", "sipCallId": "user@example.com", "lastLegDisposition": "Voicemail", "callLegCount": "1", "callTime": 24760, "ringDuration": 39, "abandonedTime": 0, "calleeHoldDurationMS": 0, "calleeHoldDuration": "00:00:00", "waitTimeMS": 0, "waitTime": "00:00:00", "departments": [ "Sales Engineering" ], "branches": [ "Central" ] }, { "dnis": "15554441212", "aaDestination": null, "callId": "1635571048345", "startTimeUTC": 1643897492897, "startTime": "2022-02-03T09:11:32.897-0500", "connectTimeUTC": 0, "connectTime": "0", "disconnectedTimeUTC": 1643899189421, "disconnectedTime": "2022-02-03T09:39:49.421-0500", "talkTimeMS": 0, "talkTime": "00:00:00", "caller": "+15551234567", "callerName": "15551234567", "callee": "CallQueue", "calleeName": "Test Queue", "direction": "Incoming", "callerId": "15551234567,+15551234567", "missed": "Missed", "abandoned": "Abandoned", "answered": "-", "answeredTime": 0, "calleeDisconnectOnHold": "", "callerDisconnectOnHold": "", "pbxId": "acmecorppbx", "sipCallId": "user@example.com", "lastLegDisposition": "Missed", "callLegCount": "1", "callTime": 1696524, "ringDuration": 197, "abandonedTime": 1696524, "calleeHoldDurationMS": 0, "calleeHoldDuration": "00:00:00", "waitTimeMS": 1696320, "waitTime": "00:28:16", "departments": [ "East Office" ], "branches": [ "East Coast" ] } ] } ``` ### Call Legs Request ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v{version}/cdr?pbxId={pbxId here}&startTime=2022-02-03 00:00:00&endTime=2022-02-03 10:00:00&timeZone=America/New_York&pageSize=50&isCallRecord=false&isSimplified=false&isConnectTime=false' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ### Call Legs Response > 📘 **Try out the CDR Legs Request** > > Jump in and try it out in the [CDR Legs Reference](/analytics/reference/call-detail-record-legs) > > This response is for isCallRecord=false For details on cdr metrics please refer to[CDR Glossary and Details](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/call-detail-record.htm) ```json { "meta": { "totalRecordCount": 2, "scrollId": "c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODM0NV8xXzE2NDM4OTc0OTI5MDM" }, "data": [ { "callId": "1635571048351", "legId": "1", "startTimeUTC": 1643898447499, "startTime": "2022-02-03T09:27:27.499-0500", "connectTimeUTC": 1643898447539, "connectTime": "2022-02-03T09:27:27.539-0500", "disconnectedTimeUTC": 1643898472253, "disconnectedTime": "2022-02-03T09:27:52.253-0500", "talkTimeMS": 0, "talkTime": "00:00:00", "caller": "+15555551220", "callerName": "MARK SMITH", "callee": "120088", "calleeName": "Jane Li", "lra": "120088", "direction": "Incoming", "parentCallId": null, "transferToCallId": null, "dnis": "12025555720", "status": "Completed", "callerDeviceId": null, "calleeDeviceId": null, "callerDeviceModel": "", "calleeDeviceModel": "", "callerId": "MARK SMITH,+15555551220", "missed": "Missed", "abandoned": "-", "answered": "-", "cause": "Ring No Answer", "callerSvcName": null, "callerSvcType": null, "calleeSvcName": "VMadvanced", "calleeSvcType": "Custom", "lraType": 1, "calleeHoldDurationMS": 0, "calleeHoldDuration": "00:00:00", "calleeDisconnectOnHold": "", "callerDisconnectOnHold": "", "pbxId": "acmecorppbx", "sipCallId": "user@example.com", "departments": [ "Sales Engineering" ], "branches": [ "Central" ], "recordServiceOn": "", "bargeServiceOn": "", "masterSlaveExts": "", "propsLastPartyDisp": "Voicemail", "accountCode": "", "aaPath": "", "callTime": 24754 }, { "callId": "1635571048345", "legId": "1", "startTimeUTC": 1643897492903, "startTime": "2022-02-03T09:11:32.903-0500", "connectTimeUTC": 1643897493100, "connectTime": "2022-02-03T09:11:33.100-0500", "disconnectedTimeUTC": 1643899189420, "disconnectedTime": "2022-02-03T09:39:49.420-0500", "talkTimeMS": 0, "talkTime": "00:00:00", "caller": "+15551234567", "callerName": "15551234567", "callee": "CallQueue", "calleeName": "Test Queue", "lra": "110081", "direction": "Incoming", "parentCallId": null, "transferToCallId": null, "dnis": "15554441212", "status": "Completed", "callerDeviceId": null, "calleeDeviceId": null, "callerDeviceModel": "", "calleeDeviceModel": "", "callerId": "14164666541,+14164666541", "missed": "Missed", "abandoned": "Abandoned", "answered": "-", "cause": "Normal", "callerSvcName": null, "callerSvcType": null, "calleeSvcName": "ACDOperatorService", "calleeSvcType": "Custom", "lraType": 4, "calleeHoldDurationMS": 0, "calleeHoldDuration": "00:00:00", "calleeDisconnectOnHold": "", "callerDisconnectOnHold": "", "pbxId": "acmecorppbx", "sipCallId": "user@example.com", "departments": [ "East Office" ], "branches": [ "East Coast" ], "recordServiceOn": "", "bargeServiceOn": "", "masterSlaveExts": "", "propsLastPartyDisp": "Missed", "accountCode": "", "aaPath": "", "callTime": 1696517 } ] } ``` > 👍 **Follow the pagination steps below to retrieve subsequent pages.** > > #### Pagination Within Work Analytics Only the /cdr endpoint is subject to pagination. This is controlled by `pageSize` and `scrollId` * `pageSize` is the number of records to return per page and is required for /cdr * `scrollId` is returned from /cdr requests providing an id for the next page of results. **Pagination Example** Assuming there will be 81 records in total. With an initial input of `pageSize=50` the returned meta data will be as follows. Note: data has been truncated to an empty array to limit the size of the example text ```json { "meta": { "totalRecordCount": 81, "scrollId": "c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODQ4Nl8xXzE2NDM5MTIzNTI0NDE" }, "data": [ ] } ``` The request for the next page would include scrollId set as the value returned in the previous request `pageKey=50&scrollId=c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODQ4Nl8xXzE2NDM5MTIzNTI0NDE` The new result set would look as follows. The returned result set would only have 31 elements. ```json { "meta": { "totalRecordCount": 81, "scrollId": "c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODM0NV8xXzE2NDM4OTc0OTI5MDM" }, "data": [ ] } ``` The request for the next page would be `pageKey=50&scrollId=c3VwZXJ0ZW5hbnRjc21fMTYzNTU3MTA0ODM0NV8xXzE2NDM4OTc0OTI5MDM` the scrollId has been set to the value returned in the previous request ```json { "meta": { "totalRecordCount": 0, "scrollId": "No Data" }, "data": [] } ``` --- ## Company Summary > 📘 Looking for live/real-time queue, agent or call-level data? See [Work Analytics — Overview](/analytics/docs/work-analytics-queue-agent-reports). [8x8 Work Analytics Historical](/analytics/reference/authentication-1) access is via this multi step process. For any of the endpoints the same process is followed. > 📘 **You will need a working API key to begin** > > You can generate API credentials from [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > The `8x8-api-key` will be the `Key` generated. For Work Analytics the Secret from Admin Console is not required. > > > 📘 > > Note: This API provides access to data from the past 2 years only in accordance with Analytics for Work data compliance policies; queries spanning more than 2 years will return only the most recent 2 years of data, and queries outside this range will return no results > > Use the following base URL during this process: * `https://api.8x8.com/analytics/work` ## Run Report ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-api key provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyy.zzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version for company summary is v1 | v1 | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | Pass the pbxId (PBX Name) of the requested pbx or comma separated list of pbxIds or `allpbxes` for all of the pbxs in the customer account. PBX names can be found [here in Admin Console](https://admin.8x8.com/company/pbx) | acmecorp,acmecorp2 | | startTime | ✓ | The interval start time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 08:30:00 | | endTime | ✓ | The interval end time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 19:00:00 | | timeZone | ✓ | [IANA Time Zones](https://www.iana.org/time-zones). Examples America/New_York, Europe/London [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | America/New_York | ### Company Summary Request > 📘 **Try out the Company Summary** > > You can try the company summary API from the [reference](/analytics/reference/company-summary) > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v{version}/compsum?pbxId={pbxId here}&startTime=2022-02-03 00:00:00&endTime=2022-02-03 10:00:00&timeZone=America/New_York' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ### Company Summary Response For details on the company summary metrics please refer to [Company Summary Glossary](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/company-summary-beta.htm#Glossary) > 📘 **Durations are in milliseconds** > > ```json [ { "pbxId": "acmecorp", "inboundTotal": 2252, "outboundTotal": 750, "externalInboundTotal": 0, "externalInboundAnswered": 0, "externalInboundAbandoned": 0, "percentExternalInboundAnswered": 0, "externalInboundMissed": 0, "externalOutboundTotal": 0, "externalOutboundAnswered": 0, "percentExternalOutboundAnswered": 0, "externalOutboundAbandoned": 0, "externalOutboundMissed": 0, "internalInboundTotal": 0, "internalInboundAnswered": 0, "internalInboundAbandoned": 0, "internalInboundMissed": 0, "internalOutboundTotal": 0, "internalOutboundAnswered": 0, "internalOutboundAbandoned": 0, "inboundAnswered": 987, "inboundAbandoned": 592, "inboundMissed": 1265, "outboundAnswered": 664, "outboundAbandoned": 86, "totalAnswered": 1754, "totalAbandoned": 732, "totalMissed": 1411, "totalRingTime": 12281452, "totalTalkTime": 573966607, "totalCall": 3215, "totalAbandonedTime": 125067986, "totalCallTime": 735627139, "avgRingTime": 6974, "avgTalkTime": 327232, "avgCallTime": 228810, "avgAbandonedTime": 179437, "totalVm": 688, "totalExtToExtAbandoned": 54, "totalExtToExtAnswered": 103, "totalExtToExt": 213, "totalExtToExtMissed": 60 }, { "pbxId": "acmecorp2", "inboundTotal": 0, "outboundTotal": 1, "externalInboundTotal": 0, "externalInboundAnswered": 0, "externalInboundAbandoned": 0, "percentExternalInboundAnswered": 0, "externalInboundMissed": 0, "externalOutboundTotal": 0, "externalOutboundAnswered": 0, "percentExternalOutboundAnswered": 0, "externalOutboundAbandoned": 0, "externalOutboundMissed": 0, "internalInboundTotal": 0, "internalInboundAnswered": 0, "internalInboundAbandoned": 0, "internalInboundMissed": 0, "internalOutboundTotal": 0, "internalOutboundAnswered": 0, "internalOutboundAbandoned": 0, "inboundAnswered": 0, "inboundAbandoned": 0, "inboundMissed": 0, "outboundAnswered": 1, "outboundAbandoned": 0, "totalAnswered": 1, "totalAbandoned": 0, "totalMissed": 0, "totalRingTime": 5158, "totalTalkTime": 2248, "totalCall": 1, "totalAbandonedTime": 0, "totalCallTime": 7429, "avgRingTime": 5158, "avgTalkTime": 2248, "avgCallTime": 7429, "avgAbandonedTime": 0, "totalVm": 0, "totalExtToExtAbandoned": 0, "totalExtToExtAnswered": 0, "totalExtToExt": 0, "totalExtToExtMissed": 0 } ] ``` --- ## Extension Summary > 📘 Looking for live/real-time queue, agent or call-level data? See [Work Analytics — Overview](/analytics/docs/work-analytics-queue-agent-reports). [8x8 Work Analytics Historical](/analytics/reference/authentication-1) access is via this multi step process. For any of the endpoints the same process is followed. > 📘 **You will need a working API key to begin** > > You can generate API credentials from [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > The `8x8-api-key` will be the `Key` generated. For Work Analytics the Secret from Admin Console is not required. > > > 📘 > > Note: This API provides access to data from the past 2 years only in accordance with Analytics for Work data compliance policies; queries spanning more than 2 years will return only the most recent 2 years of data, and queries outside this range will return no results > > Use the following base URL during this process: * `https://api.8x8.com/analytics/work` ## Run Report ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-api key provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyyyyy.zzzzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version for extension summary is v2 | v2 | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | Pass the pbxId (PBX Name) of the requested pbx or comma separated list of pbxIds or `allpbxes` for all of the pbxs in the customer account. PBX names can be found [here in Admin Console](https://admin.8x8.com/company/pbx) | acmecorp,acmecorp2 | | startTime | ✓ | The interval start time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 08:30:00 | | endTime | ✓ | The interval end time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 19:00:00 | | timeZone | ✓ | [IANA Time Zones](https://www.iana.org/time-zones). Examples America/New_York, Europe/London [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | America/New_York | ### Extension Summary Request > 📘 **Try out the Extension Summary** > > Try it out @ [Extension Summary Reference](/analytics/reference/extension-summary-v2) and see who's made more calls. > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v{version}/extsum?pbxId={pbxId here}&startTime=2022-02-03 00:00:00&endTime=2022-02-03 10:00:00&timeZone=America/New_York' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ### Extension Summary Response For details on the company summary metrics please refer to [Extension Summary Glossary](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/extensions-summary-beta.htm#Glossary) > 📘 **Durations are in milliseconds** > > ```json [ { "PbxId": "acmecorp", "Extension": "8885", "ServiceType": "UE", "FirstName": "Alice", "LastName": "Smith", "Branch": "East Coast", "Department": "Marketing", "External_Inbound_Total": 1, "External_Inbound_Answered": 0, "External_Inbound_Abandoned": 1, "Percent_External_Inbound_Answered": 0, "External_Inbound_Missed": 1, "External_Outbound_Total": 0, "External_Outbound_Answered": 0, "Percent_External_Outbound_Answered": 0, "External_Outbound_Abandoned": 0, "Internal_Inbound_Total": 0, "Internal_Inbound_Answered": 0, "Internal_Inbound_Abandoned": 0, "Internal_Inbound_Missed": 0, "Internal_Outbound_Total": 0, "Internal_Outbound_Answered": 0, "Internal_Outbound_Abandoned": 0, "Inbound_Total": 1, "Inbound_Answered": 0, "Inbound_Abandoned": 1, "Inbound_Missed": 1, "Total_Calls_To_VM": 0, "Outbound_Total": 0, "Outbound_Answered": 0, "Outbound_Abandoned": 0, "Total_Answered": 0, "Total_Abandoned": 1, "Total_Missed": 1, "Total_Ring_Time": 1195, "Total_Talk_Time": 0, "Total_Abandoned_Time": 0, "Total_Call_Time": 19048, "Avg_Ring_Time": 1195, "Avg_Talk_Time": 0, "Inbound_Talk_Time": 0, "Outbound_Talk_Time": 0, "Avg_Abandoned_Time": 0, "Email": "alice.smith", "UserName": "user@example.com" }, { "PbxId": "acmecorp2", "Extension": "441001", "ServiceType": "UE", "FirstName": "Li", "LastName": "Chan", "Branch": "Remote", "Department": "HR", "External_Inbound_Total": 1, "External_Inbound_Answered": 1, "External_Inbound_Abandoned": 0, "Percent_External_Inbound_Answered": 100, "External_Inbound_Missed": 0, "External_Outbound_Total": 0, "External_Outbound_Answered": 0, "Percent_External_Outbound_Answered": 0, "External_Outbound_Abandoned": 0, "Internal_Inbound_Total": 0, "Internal_Inbound_Answered": 0, "Internal_Inbound_Abandoned": 0, "Internal_Inbound_Missed": 0, "Internal_Outbound_Total": 0, "Internal_Outbound_Answered": 0, "Internal_Outbound_Abandoned": 0, "Inbound_Total": 1, "Inbound_Answered": 1, "Inbound_Abandoned": 0, "Inbound_Missed": 0, "Total_Calls_To_VM": 0, "Outbound_Total": 0, "Outbound_Answered": 0, "Outbound_Abandoned": 0, "Total_Answered": 1, "Total_Abandoned": 0, "Total_Missed": 0, "Total_Ring_Time": 5160, "Total_Talk_Time": 2248, "Total_Abandoned_Time": 0, "Total_Call_Time": 7408, "Avg_Ring_Time": 5160, "Avg_Talk_Time": 2248, "Inbound_Talk_Time": 0, "Outbound_Talk_Time": 0, "Avg_Abandoned_Time": 0, "Email": "user@example.com", "UserName": "li.chan" } ] ``` Fields not defined in glossary | Name | Description | |----------|--------------------------------------------------------------| | UserName | 8x8 username of the user, or "N/A" for non users | | Email | Configured email address of the user, or "N/A" for non users | --- ## Queue Agent Activity The **Queue Agent Activity** report breaks agent interaction handling down **per call queue** — the same agent-level metrics as Agent Activity, but grouped under each queue and attributed to that queue, so you can see how an agent performs in one queue versus another and whether they are a primary or overflow agent. It is served by a single endpoint: * `GET /v2/pbxes/{pbxId}/queue-agent-activity` — [Agent Activity per Queue reference](/analytics/reference/get-agent-queue-activity) ## Request **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-apikey provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | The `access_token` as a Bearer token | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyy.zzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | The opaque id of the PBX (not the PBX name). | P2DlDO1HSKe0uPdZlClziw | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | queueIds | | Restrict to specific queues (comma-separated). All queues if omitted. | q1,q2 | | queueStatuses | | Filter by queue status (comma-separated). Valid values: `AVAILABLE_IDLE`, `HANDLING`, `ALERTING`, `PAUSE`, `WRAP_UP`, `BUSY_OTHER`, `DND`, `LOGGED_OUT`, `OFFLINE`. (Primary vs overflow is the agent's role, surfaced via `overflowAgent`/`queueStatus`, not a filter value.) | HANDLING,ALERTING | | userIds | | Restrict to specific agents (comma-separated). | u1 | | userStatuses | | Filter by current user status (comma-separated). | AVAILABLE,BUSY | | siteIds | | Restrict to specific sites (comma-separated). | site1 | | startDate / endDate | | Historical window in ISO 8601. The interval cannot exceed 1 day. Omit both for the current live state. | 2026-07-01T00:00:00Z | | intraDayStart / intraDayEnd | | Optional intra-day window; only valid together with `startDate`/`endDate`. | 2026-07-01T09:00:00Z | | metrics | | Comma-separated metrics to return (see below). | acceptedInteractions,averageTalkingInteractionsTime | | includeSummary | | Defaults to `true`; pass `false` to omit the per-queue aggregated `summary` of the metrics. | false | ### Metrics Full descriptions are on the [reference page](/analytics/reference/get-agent-queue-activity). They fall into these groups: * **Volume** — `enteredInteractions`, `acceptedInteractions`, `acceptedInteractionsPercentage`, `missedInteractions`, `missedInteractionsPercentage`, `rejectedInteractions`, `rejectedInteractionsPercentage` * **Timing** (seconds) — `agentAverageTimeToAnswer`, `agentTotalTalkTime`, `averageTalkingInteractionsTime`, `averageOnHoldInteractionsTime`, `averageWrapUpTime`, `totalInteractionTime` * **Status** — `userStatusTime`, `queueStatusTime` * **Live (ongoing)** — `ongoingHandlingInteractions`, `ongoingTalkingInteractions`, `ongoingOnHoldInteractions`, `ongoingWrapUpInteractions`, `ongoingTalkingInteractionsTime`, `ongoingOnHoldInteractionsTime`, `ongoingWrapUpInteractionsTime`, `ongoingTotalHandlingInteractionsTime`, `ongoingAvgHandlingInteractionsTime`, `ongoingAvgTalkingInteractionsTime` ### Example request > 📘 **Try it out** > > You can try this endpoint from the [reference](/analytics/reference/get-agent-queue-activity). > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v2/pbxes/{pbxId}/queue-agent-activity?queueIds={queueId}&startDate=2026-07-01T00:00:00Z&endDate=2026-07-01T23:59:59Z&metrics=acceptedInteractions,missedInteractions,averageTalkingInteractionsTime' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ## Response The response groups agents by queue. Each entry in `queuesAgentActivity` identifies the queue (`pbx`, `site`, `queueId`, `queueExtension`, `queueName`, `autoLogin`) and holds a `queueAgentActivity` array of the agents working that queue. Each agent entry carries its `queueStatus` / `userStatus`, whether it is an `overflowAgent`, any current `interaction`, and the requested `metrics`. > 📘 **Durations are in seconds.** Timing and percentage values may be decimals (percentages are 0–100); only counts are integers. > > ```json { "queuesAgentActivity": [ { "pbx": "acmecorp", "site": "SanJose", "queueId": "tqteLkZOScyi0Mai9ewUlA", "queueExtension": "1000", "queueName": "Support CQ", "autoLogin": true, "queueAgentActivity": [ { "userId": "5f2c1a90-1b23-4d56-8e90-abcdef012345", "agentExtension": "1014", "agentName": "Chandler Hollow", "queueStatus": "PRIMARY", "userStatus": "AVAILABLE", "overflowAgent": false, "interaction": { "caller": "Chay Hoss", "address": "1006" }, "metrics": { "acceptedInteractions": 30, "missedInteractions": 2, "averageTalkingInteractionsTime": 138.6, "queueStatusTime": 3600.0 } } ], "summary": { "acceptedInteractions": 30, "missedInteractions": 2 } } ] } ``` ### Response fields | Field | Description | | --- | --- | | queuesAgentActivity[] | One entry per queue matching the filters | | queuesAgentActivity[].queueId / queueName / queueExtension | Queue identifier, display name and extension | | queuesAgentActivity[].autoLogin | Whether the queue uses auto-login | | queuesAgentActivity[].queueAgentActivity[] | Agents working that queue | | ...queueAgentActivity[].queueStatus / overflowAgent | The agent's role in this queue (primary vs overflow) | | ...queueAgentActivity[].interaction | The interaction the agent is currently handling, if any | | ...queueAgentActivity[].metrics | Object holding each requested metric and its value | | queuesAgentActivity[].summary | Aggregated metrics for the queue (present by default; omitted when `includeSummary=false`) | --- ## Work Analytics — Overview The Work Analytics v2 reporting API powers the Call Queue, Agent Activity, Ring Group and call-level reports shown in the 8x8 **Analytics for Work** application. Each report in this section maps to one API endpoint that you can call directly to reproduce the data behind it. These endpoints serve **both** historical and live data: supply `startDate`/`endDate` to get aggregated metrics over a past window, or omit them to get the current live state (the `ongoing*` metrics). All of these endpoints share the same base URL, authentication and a common set of filtering/time-window parameters, documented once on this page. Each report page then documents only what is specific to that endpoint (its metrics, response shape and any extra parameters). > 📘 **You will need a working API key to begin** > > You can generate API credentials from [How to get API Keys](/analytics/docs/how-to-get-api-keys). > > The `8x8-apikey` will be the `Key` generated. For Work Analytics the Secret from Admin Console is not required. > > ## Base URL Use the following base URL for every endpoint in this section: * `https://api.8x8.com/analytics/work` ## 1. Authenticate to retrieve access token You authenticate with your API key combined with the credentials of a user who has permission and access to Work Analytics. That user **does not need to be** the one who generated the API credentials. > 🚧 **User must access Analytics at least once via browser** > > The user's credentials cannot be used with the API until they have opened Analytics for Work in a browser at least once. > > ### Parameters **Method: POST** #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-apikey provided | test_key_kjdfidj238jf9123df221 | | Content-Type | ✓ | Set content type to form-urlencoded | application/x-www-form-urlencoded | #### Body | Name | Required | Description | Example | | --- | --- | --- | --- | | username | ✓ | The 8x8 username of a user with Work Analytics access privileges | [someuser@acme.fakeco](mailto:someuser@acme.fakeco) | | password | ✓ | The 8x8 password of the user with Work Analytics access privileges | Rrnp5QBW6dTbx^TP | ### Authentication Request ```bash curl --location --request POST 'https://api.8x8.com/analytics/work/v1/oauth/token' \ --header '8x8-apikey: {8x8-apikey input here}' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'username={8x8 username of user input here}' \ --data-urlencode 'password={8x8 password of user input here}' ``` ### Authentication Response ```json { "access_token": "eyJhbGciOiJSUzI1NiJ9.yyyyyyyy.zzzzzzzzzz", "token_type": "bearer", "expires_in": 1800 } ``` **Outputs for the next step:** * `access_token` * `expires_in` The token expires after `expires_in` seconds. Every report request below is authenticated by sending the `8x8-apikey` header **and** an `Authorization` header set to `Bearer {access_token}` (note the space between `Bearer` and the token). ## 2. Common request parameters Every report in this section is scoped to a PBX (`pbxId`, path parameter) and accepts the same filtering and time-window query parameters. Individual report pages list any additional parameters they support. #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-apikey provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | The `access_token` from step 1 as a Bearer token | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyy.zzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | The opaque id of the PBX (not the PBX name). | P2DlDO1HSKe0uPdZlClziw | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | siteIds | | Filter by site — comma-separated **opaque site ids** (not site names). If omitted, all sites for the PBX are returned. | vcXZjfBOT2yWxhUQHzwqwe | | queueIds | | Filter by call queue — comma-separated **opaque queue ids** (not queue names). If omitted, all queues for the PBX are returned. | JMwyX0BUTRC9uAnuAz8wfg | | startDate | | Report start date (ISO 8601). If `startDate` is entered, `endDate` is required as well. | 2026-07-01T00:00:00Z | | endDate | | Report end date (ISO 8601). If `endDate` is entered, `startDate` is required as well. | 2026-07-15T23:59:59Z | | intraDayStart | | Intra-day start time (ISO 8601, up to seconds). Requires `intraDayEnd`, and only valid together with `startDate`/`endDate`. | 2026-07-01T09:00:00Z | | intraDayEnd | | Intra-day end time (ISO 8601, up to seconds). Requires `intraDayStart`, and only valid together with `startDate`/`endDate`. | 2026-07-01T17:00:00Z | | metrics | | Comma-separated list of metrics to return. The values allowed differ per endpoint — see each report page and its reference. | enteredCalls,acceptedCalls | | includeSummary | | Defaults to `true`; pass `false` to omit the aggregated summary of the requested metrics. | false | > 📘 **Time window vs. live state** > > When `startDate`/`endDate` are supplied the report covers that historical window. When both are omitted the endpoint returns current values — the `ongoing*` live metrics (calls and agents active right now) together with aggregates over the recent default window. > > ## Reports in this section | Report | Endpoint | Reference | | --- | --- | --- | | [Call Queues](/analytics/docs/work-analytics-call-queues) | `GET /v2/pbxes/{pbxId}/call-queue-metrics` | [Get Call Queue Metrics](/analytics/reference/get-queue-metrics) | | [Agent Activity](/analytics/docs/work-analytics-agent-activity) | `GET /v2/pbxes/{pbxId}/agent-activity` | [Agent Activity](/analytics/reference/get-agent-activity) | | [Queue Agent Activity](/analytics/docs/work-analytics-queue-agent-activity) | `GET /v2/pbxes/{pbxId}/queue-agent-activity` | [Agent Activity per Queue](/analytics/reference/get-agent-queue-activity) | | [Ring Group Agent Activity](/analytics/docs/work-analytics-ring-group-agent-activity) | `GET /v2/pbxes/{pbxId}/ring-group-agent-activity/{ringGroupId}` | [Ring Group Agent Activity](/analytics/reference/get-ring-group-agent-activity) | | [Aggregated & Detailed Calls](/analytics/docs/work-analytics-aggregated-calls) | `GET /v2/pbxes/{pbxId}/calls` | [Call Queue Data Table](/analytics/reference/aggregated-3) | | [Active Calls](/analytics/docs/work-analytics-active-calls) | `GET /v2/pbxes/{pbxId}/calls/active` | [Active Calls](/analytics/reference/get-active-calls) | | [Unreturned Calls](/analytics/docs/work-analytics-unreturned-calls) | `GET /v2/pbxes/{pbxId}/calls/unreturned` | [Unreturned Calls](/analytics/reference/get-unreturned-calls) | ## Related reports These v2 endpoints serve live and recent-window data. For the older batch **summary** reports — [Company Summary](/analytics/docs/work-analytics-company-summary), [Extension Summary](/analytics/docs/work-analytics-extension-summary), the ring group summaries, and [Call Detail Records](/analytics/docs/work-analytics-call-detail-records) / [Call Legs](/analytics/docs/work-analytics-call-legs) — see the other Work Analytics report pages in this section. --- ## Ring Group Agent Activity The **Ring Group Agent Activity** report shows per-agent activity for the members of a ring group on a PBX — for each agent in the group, how long they were logged in to, and logged out of, the ring group over the reporting window. It is addressable either by the ring group's id or by its extension. Two operations back this report: * `GET /v2/pbxes/{pbxId}/ring-group-agent-activity/{ringGroupId}` — activity for a ring group identified by id. [Ring Group Agent Activity reference](/analytics/reference/get-ring-group-agent-activity) * `GET /v2/pbxes/{pbxId}/ring-group-agent-activity/by-extension/{ringGroupExtension}` — the same report, addressed by ring group extension. [By Extension reference](/analytics/reference/get-ring-group-agent-activity-by-extension) ## Request **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-apikey provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | The `access_token` as a Bearer token | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyy.zzzzzzzzzz | #### Path | Name | Required | Applies to | Description | Example | | --- | --- | --- | --- | --- | | pbxId | ✓ | both | The opaque id of the PBX (not the PBX name). | P2DlDO1HSKe0uPdZlClziw | | ringGroupId | ✓ | `/{ringGroupId}` | Id of the ring group | tqteLkZOScyi0Mai9ewUlA | | ringGroupExtension | ✓ | `/by-extension/{ringGroupExtension}` | Extension of the ring group | 2000 | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | agentExtensions | | Restrict to specific agent extensions within the ring group (comma-separated). If omitted, all agents in the group are returned. | 1006,1014 | | metrics | | Comma-separated metrics to return (see below). If omitted, all available metrics are returned. | loggedInDuration,loggedOutDuration | | startDate / endDate | | Historical window in ISO 8601. Omit both for the last 24 hours. | 2026-07-01T00:00:00Z | | intraDayStart / intraDayEnd | | Optional intra-day window; only valid together with `startDate`/`endDate`. | 09:00:00-07:00 | | sorting | | Sort field and direction. Sortable field: `agentExtension`. | agentExtension,asc | ### Metrics The report serves two duration metrics (both in milliseconds). Full descriptions are on the reference pages ([by id](/analytics/reference/get-ring-group-agent-activity), [by extension](/analytics/reference/get-ring-group-agent-activity-by-extension)): * `loggedInDuration` — total time the agent was logged in to the ring group during the reporting period. * `loggedOutDuration` — total time the agent was logged out of the ring group during the reporting period. ### Example request > 📘 **Try it out** > > You can try both endpoints from the reference pages linked above. > > ```bash # By ring group id curl --location --request GET 'https://api.8x8.com/analytics/work/v2/pbxes/{pbxId}/ring-group-agent-activity/{ringGroupId}?startDate=2026-07-01T00:00:00Z&endDate=2026-07-15T23:59:59Z&metrics=loggedInDuration,loggedOutDuration' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' # By ring group extension curl --location --request GET 'https://api.8x8.com/analytics/work/v2/pbxes/{pbxId}/ring-group-agent-activity/by-extension/{ringGroupExtension}?metrics=loggedInDuration,loggedOutDuration' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ## Response Both operations return the same shape: a `ringGroupAgentActivity` array with one entry per agent in the ring group. Each entry carries the agent's `agentExtension` and a `metrics` object holding each requested metric and its value. > 📘 **Durations are in milliseconds.** > > ```json { "ringGroupAgentActivity": [ { "agentExtension": "1014", "metrics": { "loggedInDuration": 25200000, "loggedOutDuration": 3600000 } }, { "agentExtension": "1006", "metrics": { "loggedInDuration": 18000000, "loggedOutDuration": 10800000 } } ] } ``` ### Response fields | Field | Description | | --- | --- | | ringGroupAgentActivity[] | One entry per agent in the ring group matching the filters | | ringGroupAgentActivity[].agentExtension | The agent's phone extension | | ringGroupAgentActivity[].metrics | Object holding each requested metric and its value (`loggedInDuration`, `loggedOutDuration`) | --- ## Ring Group & Ring Group Member Summaries > 🚧 **Updated Endpoints Available** > > The [Ring Group Member Summary](/analytics/docs/ring-group-member-summary) and [Ring Group Summary](/analytics/docs/wa-ring-group-summary) are dedicated endpoints to replace the previous [Ring Group & Ring Group Member Summary](/analytics/docs/work-analytics-ring-group-summary) endpoint which served both purposes > > > 📘 > > Note: This API provides access to data from the past 2 years only in accordance with Analytics for Work data compliance policies; queries spanning more than 2 years will return only the most recent 2 years of data, and queries outside this range will return no results > > You will need a working API key to begin > 📘 **You will need a working API key to begin** > > You can generate API credentials from [How to get API Keys](/analytics/docs/how-to-get-api-keys) > > The `8x8-api-key` will be the `Key` generated. For Work Analytics the Secret from Admin Console is not required. > > Use the following base URL during this process: * `https://api.8x8.com/analytics/work` ## 1. Authenticate to retrieve access token You will use your API key combined with the user credentials of a user with permission and access to Work Analytics to authenticate, this user **does not need to be** the one who generated the API credentials > 🚧 **User must access Analytics at least once via browser** > > The users credentials will not be able to leverage the API until they have used Work Analytics via browser at least once > > ### Parameters **Method: POST** #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-api key provided | test_key_kjdfidj238jf9123df221 | | Content-Type | ✓ | Set content type to form-urlencoded | application/x-www-form-urlencoded | #### Body | Name | Required | Description | Example | | --- | --- | --- | --- | | username | ✓ | The 8x8 username of a user with Work Analytics access privileges | [someuser@acme.fakeco](mailto:someuser@acme.fakeco) | | password | ✓ | The 8x8 password of the user with Work Analytics access privileges | Rrnp5QBW6dTbx^TP | ### Authentication Request ```bash curl --location --request POST 'https://api.8x8.com/analytics/work/v1/oauth/token' \ --header '8x8-apikey: {8x8-apikey input here}' \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data-urlencode 'username={8x8 username of user input here}' \ --data-urlencode 'password={8x8 password of user input here}' ``` ### Authentication Response **Response** ```json { "access_token": "eyJhbGciOiJSUzI1NiJ9.yyyyyyyyy.zzzzzzzzzzzzzzzzzz", "token_type": "bearer", "expires_in": 1800 ``` **Outputs For Next Step:** * access_token * expires_in The token will expire in the number of seconds specified in expires_in. The following steps will use the access_token as a Bearer Token form of authentication. This takes the form of the `Authorization` header being set to `Bearer access_token` (Space between Bearer and the access_token) ## 2a. Run Ring Group Summary This will return a summary for all of the Ring Groups in the specified PBXs for the duration specified. ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-api key provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyyy.zzzzzzzzzzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version for ring group summary is v2 | v2 | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | Pass the pbxId (PBX Name) of the requested pbx or comma separated list of pbxIds or `allpbxes` for all of the pbxs in the customer account | acmecorp,acmecorp2 | | startTime | ✓ | The interval start time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 08:30:00 | | endTime | ✓ | The interval end time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 19:00:00 | | timeZone | ✓ | [IANA Time Zones](https://www.iana.org/time-zones). Examples America/New_York, Europe/London [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | America/New_York | ### Ring Group Summary Request > 📘 **Try out the Ring Group Summary** > > Which group has the least missed calls, lets find out @ [Ring Group Summary Reference](/analytics/reference/ring-group-summary) > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v{version}/rgsum?pbxId={pbxId here}&startTime=2022-02-03 00:00:00&endTime=2022-02-03 10:00:00&timeZone=America/New_York' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ### Ring Group Summary Response For details on the company summary metrics please refer to [Ring Group Summary Glossary](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/ring-group-summary.htm#Glossary) > 📘 **Durations are in milliseconds** > > ```json [ { "pbxId": "acmecorp", "site": "East", "name": "Marketing", "extension": "100027", "totalMembers": 0, "totalInbound": 1, "totalAbandoned": 1, "totalAnswered": 0, "totalMissed": 1, "totalCallsToVM": 0, "totalAdvanced": 0, "totalRgTime": 5475, "totalTalkTime": 0, "totalCalls": 1, "avgAbandonedTime": 5475, "avgRgTime": 5475, "avgRingTime": 283, "avgTalkTime": 0, "totalAbandonedTime": 5475, "totalRingTime": 283 }, { "pbxId": "acmecorp2", "site": "West", "name": "Sales", "extension": "100055", "totalMembers": 0, "totalInbound": 12, "totalAbandoned": 0, "totalAnswered": 0, "totalMissed": 12, "totalCallsToVM": 12, "totalAdvanced": 0, "totalRgTime": 0, "totalTalkTime": 0, "totalCalls": 12, "avgAbandonedTime": 0, "avgRgTime": 0, "avgRingTime": 0, "avgTalkTime": 0, "totalAbandonedTime": 0, "totalRingTime": 0 } ] ``` ## 2b. Run Ring Group Member Summary This will return a summary each member of each Ring Group in the specified PBXs for the duration specified. > 📘 **Ring Group Member Summary Reference** > > You can check out [Ring Group Member Summary Reference](/analytics/docs/ring-group-member-summary) but you won't be able to try it yet. > > ### Parameters **Method:** GET #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-api key provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | Pass the access_token returned from the authentication request as a Bearer token `Bearer {access_token}` | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyyy.zzzzzzzzzzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | version | ✓ | The current version for ring group summary is v2 | v2 | #### Query | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | Pass the pbxId (PBX Name) of the requested pbx or comma separated list of pbxIds or `allpbxes` for all of the pbxs in the customer account | acmecorp,acmecorp2 | | startTime | ✓ | The interval start time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 08:30:00 | | endTime | ✓ | The interval end time for CDR searches - the format is YYYY-MM-DD HH:MM:SS. | 2022-10-20 19:00:00 | | timeZone | ✓ | [IANA Time Zones](https://www.iana.org/time-zones). Examples America/New_York, Europe/London [Wikipedia Time Zone List](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | America/New_York | | extId | ✓ | The extension number of the ring group. Only a single ring group extension can be specified. | 100169 | | collapse | ☐ | This true or false value determines whether to collapse ring group member summaries into a single summary, even if the user information has changed during the date range selected; otherwise a distinct summary will be created for each distinct set of member data. Default is true | true | ### Ring Group Member Summary Request > 📘 **Try out the Ring Group Member Summary** > > How does average talk time compare across users? Lets find out @ [Ring Group Member Summary Reference](/analytics/docs/ring-group-member-summary) > > ```bash curl --location --request GET 'https://api.8x8.com/analytics/work/v{version}/rgsum?pbxId={pbxId here}&startTime=2022-01-03 00:00:00&endTime=2022-05-03 10:00:00&timeZone=America/New_York&extId=100169' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' ``` ### Ring Group Member Summary Response For details on the company summary metrics please refer to [Ring Group Member Summary Glossary](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/ring-group-summary.htm#Glossary) > 📘 **Durations are in milliseconds** > > ```json [ { "pbxId": "acpmecorp", "site": "West", "firstName": "Marty", "lastName": "McFly", "ringGroupName": "Management", "ringGroupExtension": "100169", "extension": "100065", "totalAnswered": 0, "totalAdvanced": 5, "totalTalkTime": 0, "totalRingTime": 53821, "totalCalls": 5, "avgRingTime": 10764, "avgTalkTime": 0, "offered": 5 }, { "pbxId": "acpmecorp", "site": "West", "firstName": "Jane", "lastName": "Li", "ringGroupName": "Managementz", "ringGroupExtension": "100169", "extension": "100066", "totalAnswered": 2, "totalAdvanced": 1, "totalTalkTime": 148789, "totalRingTime": 31380, "totalCalls": 3, "avgRingTime": 10460, "avgTalkTime": 74394, "offered": 3 } ] ``` --- ## Unreturned Calls The **Unreturned Calls** report surfaces calls into the PBX that were **not answered and have not been called back** — abandoned or missed callers who are still waiting for a call back. It aggregates those calls per caller, lets you list the distinct callers and callees involved (to drive report filters), and drills down to the individual unreturned call records. Five operations back this report: * `GET /v2/pbxes/{pbxId}/calls/unreturned` — unreturned calls aggregated per caller. [Unreturned Calls reference](/analytics/reference/get-unreturned-calls) * `GET /v2/pbxes/{pbxId}/calls/unreturned/callees` — distinct callees that appear in the unreturned calls. [Callees reference](/analytics/reference/get-unreturned-callees) * `GET /v2/pbxes/{pbxId}/calls/unreturned/callers` — distinct callers whose calls were not returned. [Callers reference](/analytics/reference/get-unreturned-callers) * `GET /v2/pbxes/{pbxId}/calls/unreturned/detailed` — the per-call drill-down. [Detailed reference](/analytics/reference/get-unreturned-detailed) * `POST /v2/pbxes/{pbxId}/calls/unreturned/detailed` — the same drill-down with the filters in a JSON body. [Detailed (POST) reference](/analytics/reference/post-unreturned-detailed) ## Request **Method:** GET (all reads) and POST (the detailed drill-down variant) #### Headers | Name | Required | Description | Example | | --- | --- | --- | --- | | 8x8-apikey | ✓ | The 8x8-apikey provided | test_key_kjdfidj238jf9123df221 | | Authorization | ✓ | The `access_token` as a Bearer token | Bearer eyJhbGciOiJSUzI1NiJ9.yyyyyyyy.zzzzzzzzzz | #### Path | Name | Required | Description | Example | | --- | --- | --- | --- | | pbxId | ✓ | The opaque id of the PBX (not the PBX name). | P2DlDO1HSKe0uPdZlClziw | ### Aggregated unreturned calls — `GET .../calls/unreturned` Returns one row per caller, grouping the callee they last tried to reach, the number of unreturned `attempts`, and the `latestStartTime` of the most recent attempt. | Query param | Required | Description | Example | | --- | --- | --- | --- | | callerAddresses | | Restrict to specific caller addresses (comma-separated). | 1006,1008 | | calleeAddresses | | Restrict to specific callee addresses (comma-separated). | 1014 | | search | | Free-text search across caller/callee identity fields. | Hoss | | startDate / endDate | | Historical window in ISO 8601. Omit both for the last 24 hours. | 2026-07-01T00:00:00Z | | intraDayStart / intraDayEnd | | Optional intra-day window; only valid together with `startDate`/`endDate`. | 09:00:00-07:00 | | sorting | | Optional. Sort field and direction. | attempts,desc | | fields | | Comma-separated response fields to return (see below). If omitted, all available fields are returned. | callerName,attempts,latestStartTime | > 📘 **Selecting fields, not metrics** > > Like the other calls endpoints, Unreturned Calls selects which **response fields** to return via the `fields` parameter (not a `metrics` parameter). The full list of selectable fields and their descriptions is on the [reference page](/analytics/reference/get-unreturned-calls). > > The aggregated selectable fields fall into these groups (full descriptions on the [reference page](/analytics/reference/get-unreturned-calls)): * **Caller** — `callerId`, `callerAddress`, `callerName`, `callerDeviceId`, `callerDeviceModel`, `callerServiceName`, `callerServiceType` * **Callee** — `calleeAddress`, `calleeName`, `calleeDeviceId`, `calleeDeviceModel`, `calleeServiceName`, `calleeServiceType` * **Site** — `siteId`, `siteName` * **Aggregation** — `attempts`, `latestStartTime` ### Callees and callers — `GET .../callees` and `GET .../callers` These return the distinct participants in the unreturned calls, for driving the report's callee / caller filters. Both are paged and sorted (sortable fields: `name`, `address`). | Query param | Required | Applies to | Description | Example | | --- | --- | --- | --- | --- | | siteIds | | callees, callers | Restrict to specific sites (comma-separated). | site1 | | calleeAddresses | | callers | Restrict to callers that tried to reach specific callee addresses. | 1014 | | search | | callees, callers | Free-text search across name/address. | Hollow | | startDate / endDate / intraDayStart / intraDayEnd | | callees, callers | Time filter (as above). | 2026-07-01T00:00:00Z | | paging | | callees, callers | Optional. Page controls — limit and cursor. | 50 | | sorting | | callees, callers | Optional. Sort field and direction. | name,asc | ### Detailed unreturned calls — `GET` and `POST .../detailed` Returns the individual unreturned call records for the requested caller addresses. `callerAddresses` is **required** (at least one). The GET variant takes the filters as query parameters; the POST variant takes the same filters in a JSON body. | Query param (GET) | Required | Description | Example | | --- | --- | --- | --- | | callerAddresses | ✓ | Caller addresses to drill into (comma-separated). | 1006,1008 | | calleeAddresses | | Restrict to specific callee addresses. | 1014 | | search | | Free-text search across caller/callee identity fields. | Hoss | | startDate / endDate / intraDayStart / intraDayEnd | | Time filter (as above). | 2026-07-01T00:00:00Z | | paging | | Optional. Page controls — limit and cursor. | 50 | | sorting | | Optional. Sort field and direction. | startTime,desc | | fields | | Comma-separated response fields to return. If omitted, all available fields are returned. | callId,callerName,startTime,outcome | The detailed selectable fields add the call-level attributes on top of the caller/callee groups: `callId`, `transferToCallId`, `workGroupId`, `workGroupExtension`, `workGroupType`, `startTime`, `totalDuration`, `did`, `outcome`, `labels`. See the [Detailed reference](/analytics/reference/get-unreturned-detailed) for the complete list and descriptions. > 📘 **POST body overrides query params** > > `POST .../detailed` accepts the same filters, time range, sorting and paging in a JSON body — convenient when the caller-address list is long. Any value present in the body **overrides** the equivalent query parameter, and both variants return the same `DetailedUnreturnedCallsResponse`. See the [Detailed (POST) reference](/analytics/reference/post-unreturned-detailed). > > ### Example requests > 📘 **Try it out** > > You can try each of these endpoints from the reference pages linked above. > > ```bash # Aggregated per-caller unreturned calls curl --location --request GET 'https://api.8x8.com/analytics/work/v2/pbxes/{pbxId}/calls/unreturned?sorting=attempts,desc&fields=callerName,attempts,latestStartTime' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' # Detailed drill-down for specific callers (POST) curl --location --request POST 'https://api.8x8.com/analytics/work/v2/pbxes/{pbxId}/calls/unreturned/detailed' \ --header 'Authorization: Bearer {access_token here}' \ --header '8x8-apikey: {8x8-apikey input here}' \ --header 'Content-Type: application/json' \ --data '{"callerAddresses":["1006","1008"],"startDate":"2026-07-01T00:00:00Z","endDate":"2026-07-15T23:59:59Z","sortingField":"startTime","sortingDirection":"DESC","limit":50}' ``` ## Response The aggregated endpoint returns a `calls` array; each entry groups a `caller`, the `calleeData` they last tried to reach, the `siteData` of that callee, the number of unreturned `attempts` and the `latestStartTime`. > 📘 **Durations (e.g. `totalDuration`) are in seconds; timestamps are epoch milliseconds.** > > ```json { "calls": [ { "caller": { "id": "Chay Hoss,1006", "address": "1006", "name": "Chay Hoss", "deviceModel": "voo8x8" }, "calleeData": { "address": "1014", "name": "Chandler Hollow", "deviceModel": "voo8x8" }, "siteData": { "id": "site-1", "name": "Headquarters" }, "attempts": 3, "latestStartTime": 1717751880000 } ] } ``` The `/callees` and `/callers` endpoints return a `participants` array of `{ name, address }`: ```json { "participants": [ { "name": "Chandler Hollow", "address": "1014" }, { "name": "Support Queue", "address": "1000" } ] } ``` The detailed endpoints (GET and POST) return a `records` array; each entry is one unreturned call with its `callId`, `caller`/`callee`, the `workGroup` (queue or ring group) it went through, timings and `outcome`: ```json { "records": [ { "callId": "1717751880000", "transferToCallId": "1717751890000", "caller": { "id": "Chay Hoss,1006", "address": "1006", "name": "Chay Hoss" }, "callee": { "address": "1014", "name": "Chandler Hollow" }, "workGroup": { "id": "tqteLkZOScyi0Mai9ewUlA", "extension": "1000", "groupType": "CALL_QUEUE" }, "startTime": 1717751880000, "totalDuration": 54.88, "did": "1000", "outcome": "ABANDONED", "labels": ["WAITING", "ALERTING", "ABANDONED"] } ] } ``` For the complete list of selectable fields and their descriptions, see the [Unreturned Calls reference](/analytics/reference/get-unreturned-calls) and the [Detailed reference](/analytics/reference/get-unreturned-detailed). --- ## 8x8 Analytics for Contact Center Historical Metrics API import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; As a contact center supervisor, you may need to assess the performance of your agents. For example: - Analyze an agent’s call traffic queue - Drill down and analyze an agent’s activity history and get monthly, weekly, and hourly data - Determine the number of phone calls offered, accepted, rejected, or abandoned by an agent - Identify the total call handling time during the past hour The 8x8 Analytics for Contact Center Historic Metrics API offers an entire suite of historical reports for agent interactions, agent status, and queue interactions. The Historical Metrics API is an asynchronous API so it can be used to generate, view, and download a custom report from the Historical Reporting database. **Note**: The Historical Metrics API uses either the **v1**, **v2**, **v3**, **v4**, **v5**, **v6**, **v7** or **v8** path for the following URLs: ``` https://api.8x8.com/analytics/cc/v1/historical-metrics/ https://api.8x8.com/analytics/cc/v2/historical-metrics/ https://api.8x8.com/analytics/cc/v3/historical-metrics/ https://api.8x8.com/analytics/cc/v4/historical-metrics/ https://api.8x8.com/analytics/cc/v5/historical-metrics/ https://api.8x8.com/analytics/cc/v6/historical-metrics/ https://api.8x8.com/analytics/cc/v7/historical-metrics/ https://api.8x8.com/analytics/cc/v8/historical-metrics/ ``` _** For step-by-step instructions on using the Historical Metrics API refer to the [Historical Metrics Summary Guide ](doc:cc-historical-analytics-summary-report) or the [Historical Metrics Detailed Report Guide](doc:cc-historical-analytics-detailed-report)**_. ## **Authentication** You can try out this API through request authentication using your client credentials. Refer to [How to get API credentials](doc:how-to-get-api-keys) and [how to authenitcate](doc:oauth-authentication-for-8x8-xcaas-apis) for more information. All requests must be made over HTTPS - calls made over HTTP will fail. **Note**: The Historical Metrics API uses the **v1**, **v2**, **v3**, **v4**, **v5**, **v6**, **v7** or **v8** path for the following URL: - **`GET`** request: **`https://api.8x8.com/analytics/cc/{version}/historical-metrics/`** This API uses OAuth 2 with the client credentials grant flow. Security Scheme Type: oauth2 OAuth Flow (clientCredentials): Token URL: https://api.8x8.com/oauth/v2/token Scopes: read: Grants read access write: Grants write access admin: Grants access to admin operations Security Scheme Type: http HTTP Authorization Scheme: bearer Bearer format: access_token Contact Analytics Team: [vcc-analytics@8x8.com](mailto:vcc-analytics@8x8.com) URL: [https://www.8x8.com/](https://www.8x8.com/) Terms of Service {'https://www.8x8.com/terms-and-conditions'} --- ## 8x8 Analytics for Contact Center Real-time Metrics API import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; The 8x8 Analytics for Contact Center Real-time Metrics API enables you to obtain the latest, real-time statistical data on queues and agents queue interactions. In addition to real-time data (which is refreshed every 5 seconds) you can also obtain data for select 15 or 30-minute intervals. The Real-time API supports both JSON and XML file response formats. You can switch between both formats using the `Accept` header. **Note**: the Real-time metrics endpoint is available on the following URLs: ``` https://api.8x8.com/analytics/cc/v1/realtime-metrics/ https://api.8x8.com/analytics/cc/v2/realtime-metrics/ https://api.8x8.com/analytics/cc/v3/realtime-metrics/ https://api.8x8.com/analytics/cc/v4/realtime-metrics/ https://api.8x8.com/analytics/cc/v5/realtime-metrics/ ``` _**This page is in the Beta stage. For step-by-step instructions on using the Real-time API refer to the [Real-time Metrics Use Case](https://8x8gateway-8x8apis.apigee.io/real-time-metrics-use-case)**_. ## **Authentication** You can try out this API through request authentication using your client credentials. Refer to [How to get API credentials](doc:how-to-get-api-keys) and [how to authenitcate](doc:oauth-authentication-for-8x8-xcaas-apis). All requests must be made over HTTPS - calls made over HTTP will fail. **Note**: The Real Time Metrics API uses either the **v1**, **v2**, **v3**, **v4** or **v5** path for the following URLs: - Access token: **`https://api.8x8.com/oauth/v2/token`** - **`GET`** request: **`https://api.8x8.com/analytics/cc/{version}/realtime-metrics/`** This API uses OAuth 2 with the client credentials grant flow. Security Scheme Type: oauth2 OAuth Flow (clientCredentials): Token URL: https://api.8x8.com/oauth/v2/token Scopes: read: Grants read access write: Grants write access admin: Grants access to admin operations Security Scheme Type: http HTTP Authorization Scheme: bearer Bearer format: access_token Contact Analytics Team: [vcc-analytics@8x8.com](mailto:vcc-analytics@8x8.com) URL: [https://www.8x8.com/](https://www.8x8.com/) Terms of Service {'https://www.8x8.com/terms-and-conditions'} --- ## Get Aggregated Calls import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This endpoint returns the summary of calls for selected time period and pbx --- ## Retrieve all agents with all specified metrics within a tenant. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieve all agents with all specified metrics within a tenant. --- ## Audit Records API import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; This API allows administrators to retrieve audit records in JSON format. It retrieves audit records with **create**, **delete** and **update** events on following entities in Platform. * Call forwarding rules * Call queue * Phone Number * Extension * Ring group * User basic information * Auto attendant basic information _**For step-by-step instructions on using the Audit API refer to the [Audit API Summary Guide ](https://docs.google.com/document/d/1rtGbpvCHKtR19fqPYbPh-CA6YTQkeubKgDl6r-b9N4M/edit?usp=sharing)**_. Security Scheme Type: apiKey Header parameter name: x-api-key Contact [api@8x8.com](mailto:api@8x8.com) URL: [https://www.8x8.com/](https://www.8x8.com/) Terms of Service {'https://www.8x8.com/terms-and-conditions'} --- ## Authentication(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Generate Bearer token for use with subsequent requests --- ## (DEPRECATED) Call Detail Record Legs import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: [Call Detail Record Legs](/analytics/reference/call-detail-record-legs) and [Call Detail Records](/analytics/reference/call-detail-records) should be used. This endpoint is deprecated. --- ## Call Detail Record Legs import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; The Analytics for 8x8 Work Call Detail Record (CDR) Legs endpoint enables you to see the entire journey for a subject call from start to finish across multiuple legs. See [CDR Glossary](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/call-detail-record.htm) for details. For a single row per call use the [Call Detail Records](/analytics/reference/call-detail-records) endpoint --- ## Call Detail Records(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; **Not currently Testable from here** The Analytics for 8x8 Work Call Detail Record (CDR) endpoint enables you to see the entire journey for a subject call from start to finish. See [CDR Glossary](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/call-detail-record.htm) for details. --- ## Cancel a running bulk download job by zip file name. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method cancels a running bulk download job by the zip file name. --- ## The Speech Analytics category count import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the category count --- ## Create Detailed Report import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This endpoint initializes Detailed Report Access. For step-by-step instructions on using the Historical Metrics API refer to the [CC Historical Analytics Detailed Report](/analytics/docs/cc-historical-analytics-detailed-report) Guide. A report can be either defined as filtering, metrics, time interval, or another type. --- ## Detailed Report Data import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method returns the actual detailed report data. The returned report is paginated and the data is returned if the report has a DONE status. The report page size and numbering can be specified. --- ## Report Format Details import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method obtains grouping, filtering, and metrics information about a specific report type. --- ## List Report Types import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method obtains the list of valid report-types, along with the options for grouping, filtering, or applying metrics that are specific for each report type. The list includes `script-paths` from v8 onward. --- ## Create Report import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This endpoint initializes Report Access. For step-by-step instructions on using the Historical Metrics API refer to the [CC Historical Analytics Summary Report](/analytics/docs/cc-historical-analytics-summary-report) Guide. A report can be either defined as grouping, filtering, metrics, time interval, or another type. **Script Paths variant** (`type: script-paths`, v8+) uses a dedicated request shape (`ScriptPathsReportDefinitionRequest`). See [Script Paths Report](/analytics/docs/cc-historical-analytics-summary-report#9-script-paths-report) in the guide for its request shape, restrictions, and CSV output format. --- ## Report Data import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method returns the actual report data. The returned report is paginated and the data is returned if the report has a DONE status. The report page size and numbering can be specified. **Not supported for `script-paths` reports** — such requests return **400 Bad Request**. Use `/{id}/download` instead. --- ## Report Details import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method provides information associated with a previously created report request. It includes all the parameters specified in the request. The value specified as \{id\} identifies which report request is displayed. --- ## Report Download import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method returns report data as a downloadable file. Data can only be downloaded if the report has a DONE status. The first entry in the returned file specifies the names of the columns. For `script-paths` reports, the response is CSV only (XLSX is not supported). Columns: `Path ID`, `Parent Path ID`, `Script ID`, `Script Name`, `Node Type`, `Node Label`, `Depth`, `Count`, `Terminal`. --- ## Report Links import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method returns all available links related to a previously created report. A link to the Report Status is always included in the return. If the status of the report is DONE, links to Report Data and Report Download are also included. For `script-paths` reports, only the Report Download link is included when status is `DONE` (no Report Data link). --- ## Report Status import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; After a report is created, the status of the report can be checked periodically. The report can be either classified as IN_PROGRESS, DONE, or FAILED. --- ## Individual Agent Statistics by Group import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method retrieves real-time metrics for a specific agent in a group. The agent ID, agent name and a list of metric objects. A metric object contains a `key` field with a label (description) of the metric and a `value`. The response is paginated. --- ## Individual Agent Statistics by Queue import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method retrieves real-time metrics for a specific agent in a queue. The agent ID, agent name and a list of metric objects. A metric object contains a `key` field with a label (description) of the metric and a `value`. The response is paginated. --- ## Agent Statistics by Group import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method retrieves real-time metrics for a list of agents in a selected group. By default, all agents from the given group will be returned. The response can be further filtered and is paginated. --- ## Agent Statistics by Queue import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method retrieves real-time metrics for a list of agents in a select queue. By default, all agents from the given queue will be returned. The response can be further filtered and is paginated. --- ## Individual Group Statistics import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method retrieves real-time metrics for a specific group. The response returns an object which contains the group ID, group name and a list of metric objects. The metric contains a key field with a label or description as well as a value field. --- ## Group Statistics import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method retrieves real-time metrics for a list of groups. By default, the metrics for all groups are returned. The result can be filtered and paginated. --- ## Individual Queue Metrics import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method retrieves real-time metrics for a specific queue. The response returns an object which contains the queue ID, queue name and a list of metric objects. The metric contains a key field with a label or description as well as a value field. --- ## Queue Statistics import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method retrieves real-time metrics for a list of queues. By default, the metrics for all queues are returned. The result can be filtered and paginated. --- ## Removes tasks from a download request. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method clears your task queue for bulk download requests. --- ## Cloud Storage Service Public API import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; 8x8’s Cloud Storage Service (CSS) offers you a single point of access for Virtual Office PBX telephone, Virtual Contact Center call recordings, Virtual Contact Center screen recordings and Virtual Meetings. You can access and download these data objects for your further analysis and use. You can also embed your data in other interfaces without a requirement to copy or move the underlying data. For example: * Make collected data available to customers in your client portal * Integrate call recording data into CRM applications to associate with customer interaction records * Enable deletion of media (and or metadata) to allow customers to meet internal requirements to cap data retention based on workflows, policies, or compliance needs You can obtain your stored data by either: * Querying for, and then downloading individual data objects * Bulk downloading all of your data using a Zip file utility (Note that the limit on bulk file downloads is 2 GB.) ***Note: This page is in the Beta stage. Contact your [8x8 representative](mailto:ro-rec@8x8.com) for more information on API use.*** # **Authentication** You can try out this API through request authentication using your client credentials. Refer to [Client Credentials](/analytics/docs/how-to-get-api-keys) on the [Getting Started](/tech-partner/docs/getting-started) page for more information. All requests must be made over HTTPS - calls made over HTTP will fail. # **Regions** API resources are available for each geographical region in which they are provisioned. The base URLs for select regions are: URL Region https://api.8x8.com/storage/us-west/v1Western US https://api.8x8.com/storage/us-east/v1Eastern US https://api.8x8.com/storage/uk/v1United Kingdom https://api.8x8.com/storage/ap/v1Australia https://api.8x8.com/storage/ca/v1Canada Security Scheme Type: http HTTP Authorization Scheme: bearer Bearer format: access_token Contact Recordings Team: [ro-rec@8x8.com](mailto:ro-rec@8x8.com) Terms of Service {'https://www.8x8.com/terms-and-conditions'} --- ## Company Summary(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; With the Analytics for 8x8 Work Company Summary (/compsum) endpoint you can obtain summary information about call activity that occurred within your enterprise for a given time period. The service returns a single record with key call metrics see [Company Summary Glossary](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/company-summary-beta.htm#Glossary) for details --- ## Customer 360 API import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; The Customer 360 API provides unified access to customer interaction history and insights across all 8x8 contact center channels. Given a customer identity (email, phone number, contact ID, or account ID), the API returns a list of interactions along with aggregated sentiment and topic insights. ## Authentication All endpoints require an 8x8 API key obtained from the 8x8 Admin Console. Pass the key in the `x-api-key` request header: ``` x-api-key: ``` Refer to [How to get API Keys](/analytics/docs/how-to-get-api-keys) for instructions on creating an API key. ## Regions The API is available in four regions. Use the base URL corresponding to the region where your tenant is provisioned: | Region | Base URL | |---|---| | Phoenix (US) | `https://api.8x8.com/cidp-customer-360/us` | | London (UK) | `https://api.8x8.com/cidp-customer-360/uk` | | Toronto (Canada) | `https://api.8x8.com/cidp-customer-360/ca` | | Sydney (Australia) | `https://api.8x8.com/cidp-customer-360/ap` | ## Search Strategies The API supports four mutually exclusive search strategies. You must provide exactly one identity field per request: | Strategy | Required | Optional | Forbidden | |---|---|---|---| | Contact ID | `contactId`, `crmId` | — | — | | Account ID | `accountId` | `crmId` | — | | Email | `email` | — | `crmId` | | Phone Number | `phoneNumber` | — | `crmId` | Only the native CRM is supported. Set `crmId` to `native`. ## Time Range If `startTime` and `endTime` are omitted, a default window of 1 year ending at the current time is applied. Times must be in ISO-8601 format with timezone (e.g. `2025-08-15T10:30:00-05:00`). ## Response Fields ### interactions A list of individual interactions matching the search criteria. Each interaction includes: - `interactionId` — Unique identifier for the interaction - `mediaType` — Channel type: `PHONE`, `EMAIL`, `CHAT`, or `VOICEMAIL` - `direction` — `INBOUND` or `OUTBOUND` - `productType` — 8x8 product that handled the interaction: `CC` (Contact Center), `UC` (Unified Communications), or `ENGAGE` - `startedAt` / `endedAt` — Unix epoch milliseconds - `sentiment` — Overall sentiment: `POSITIVE`, `NEUTRAL`, or `NEGATIVE` - `topics` — List of topics detected in the interaction, each with a name and match count - `wrapUpCodes` — Agent wrap-up codes applied at the end of the interaction - `queueName` — Name of the queue that handled the interaction - `outcomeLabel` — Outcome label assigned to the interaction - `interactionLabels` — Labels applied to the interaction ### insights Aggregated analysis across all returned interactions: - `aggregatedSentiments` — Overall customer, agent, and combined sentiment across all interactions - `aggregatedTopics` — Topic frequency breakdown showing which topics appeared most often and in what percentage of interactions 8x8 API key obtained from the 8x8 Admin Console. Security Scheme Type: apiKey Header parameter name: x-api-key --- ## Deletes a single interaction. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Removes the interaction and all associated metadata. This action cannot be undone. --- ## Deletes custom field data import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Updates or removes the custom field contained data for user customizable fields. Only the custom fields **1** to **15** are editable. --- ## Get Call Details import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This endpoint returns the journey of a single interaction in the specified timeframe. --- ## Request bulk download by .zip filename. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method enables access to a single zipped file that was generated from a bulk download request and is ready for retrieval. --- ## Download content for the given metadata. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method retrieves an object using the objectId specification for a given record. --- ## Download content for the given metadata.(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method retrieves an object using the **`objectId`** specification for a given record. --- ## Returns the status of a single bulk download job request by zip filename. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method returns the status of a single bulk download job request by zip filename. --- ## Returns the status of all bulk download job requests. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method returns the status of all bulk download job requests. --- ## The evaluation details specified by ID. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves a single evaluation which consists of the template section and answered questions. --- ## The evaluation count import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the evaluation count --- ## Evaluation collection import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves evaluations collection --- ## (DEPRECATED) Extension Summary v1 import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: [Extension Summary V2](/analytics/reference/extension-summary-v-2) should be used. This endpoint is deprecated. --- ## Extension Summary v2 import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; The Extension Summary (/extsum) report provides a detailed summary of call activity for any user’s extension in your enterprise’s phone system. When you integrate with the /extsum endpoint you can see aggregate call volume and how calls were handled at every user level regardless of how the call arrived at the extension. Using this report, you can track an employee’s number of answered, abandoned, and missed calls for productivity evaluation. You can sort historical data according to the total number of received calls and compare it to the users who are handling them. See [Extension Summary Glossary](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/extensions-summary-beta.htm#Glossary) for details --- ## Get Active Calls import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns the calls that are currently active (in progress) for the given PBX at the moment of the query. This is a real-time snapshot, so no date-range filtering applies. The same endpoint is also served under `/analytics/work/v2/pbxes/{pbxId}/calls/active`. --- ## Get Agent Activity Metrics import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This endpoint returns the agent activity metrics for the selected pbx, grouped by agent. The response can be further filtered by a list of queues, sites, agents, user statuses. If no date range is provided, the metrics reflect the agent activity in the last 24 hours. --- ## Get Agent Activity Metrics per Queues import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This endpoint returns the agent activity metrics for the selected pbx, grouped by queue and agent. The response can be further filtered by a list of queues, sites, agents, user or queue statuses. If no date range is provided, the metrics reflect the agent activity in the last 24 hours. --- ## Get audit records. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieve audit records in JSON format --- ## Get interaction insights import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves interaction insights for a tenant, including interactions list and speech analytics. Supports four search strategies: - `contactId` + `crmId` (both required) - `accountId` (crmId optional) - `email` (crmId must not be provided) - `phoneNumber` (crmId must not be provided) If `startTime` and `endTime` are omitted, a default time window is applied (1 year). --- ## Returns the list of agents for the given pbx. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns the list of agents for the given pbx. --- ## Returns the list of queues for the given pbx. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns the list of queues for the given pbx. --- ## Returns the list of queues for the given pbx and site. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns the list of queues for the given pbx and site. --- ## Returns the list of sites of a pbx import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns a list of sites of a pbx. --- ## Returns all pbxes of a customer import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This endpoint returns all the PBXes available for a customer --- ## Get Call Queue Metrics import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This endpoint returns the call queue metrics, for the selected PBX. The response can be further filtered by a list of queues, sites. If no date range is provided, the metrics reflect the activity on queues in the last 24 hours. --- ## Get Ring Group Agent Activity by Extension import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns per-agent activity for the members of a ring group on the given PBX, identified by the ring group **extension** instead of its id. Behaves identically to `GET /ring-group-agent-activity/{ringGroupId}` and returns the same response shape. --- ## Get Ring Group Agent Activity import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns per-agent activity for the members of a ring group on the given PBX, identified by the ring group id. Each row reports an agent's extension and the requested metrics — how long the agent was logged in to, and logged out of, the ring group over the reporting window. If no date range is provided, the metrics reflect the last 24 hours. --- ## Get transcript summaries import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves transcript summaries for one or more interaction IDs. Pass one or more `interactionId` query parameters. The API returns successfully retrieved summaries and lists any IDs that could not be retrieved as partial failures. A maximum of 50 interaction IDs can be provided per request. --- ## Get Unreturned Call Callees import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns the distinct callees (the parties that were called but not reached) that appear in the unreturned calls for the given PBX. Useful for populating a callee filter on the unreturned-calls report. The same endpoint is also served under `/v2`. --- ## Get Unreturned Call Callers import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns the distinct callers whose calls were not returned for the given PBX, optionally scoped to the callees they tried to reach. Useful for populating a caller filter on the unreturned-calls report. The same endpoint is also served under `/v2`. --- ## Get Unreturned Calls import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns unreturned calls for the given PBX, aggregated per caller — calls into the PBX that were not answered and have not been called back. Each row groups a caller with the callee they last tried to reach, the number of unreturned attempts and the most recent attempt time. The same endpoint is also served under `/analytics/work/v2/pbxes/{pbxId}/calls/unreturned`. --- ## Get Detailed Unreturned Calls import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Returns the individual unreturned call records for the given PBX and the requested caller addresses — the per-call drill-down behind the aggregated unreturned-calls view. The same endpoint is also served under `/v2`. --- ## Retrieve realtime metrics for all agents in multiple queues. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieve realtime metrics for all agents in multiple queues. --- ## Returns the content of a specified bucket. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method returns all of the contents contained within a specific bucket. --- ## Find an object by it's ID. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Use this method to retrieve an object by its ID. --- ## The requested interaction media file download import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves audio, video, mixed audio (MP3), and video (MP4) Transcoding (bit rate, sample rate and channel) options are available for audio files --- ## This method queries a single interaction transcription record. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves available transcriptions for the selected interaction --- ## Interaction count. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method retrieves the interactions count for the specified interaction type. Unless the interaction type is specified, all the interaction types are retrieved. --- ## The Interaction collection import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method retrieves interaction collections. --- ## Query a single interaction attached label import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the collection of labels attached to the interaction --- ## The Speech Analytics category list import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the category count --- ## Query the attached notes for a single interaction import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieve the notes attached to an interaction --- ## Get Detailed Unreturned Calls (POST) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Same result as `GET /calls/unreturned/detailed`, but the filters, time range, sorting and paging are supplied in a JSON request body instead of query parameters — convenient when the caller-address list is long. Any value present in the body overrides the equivalent query parameter; query parameters supplied alongside the body are used only as fallbacks. Returns the same `DetailedUnreturnedCallsResponse` as the GET variant. The same endpoint is also served under `/v2`. --- ## This method purges a single interaction. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Removes the interaction and all associated metadata. **This action cannot be undone.** --- ## Quality Management and Speech Analytics API import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; The Quality Management and Speech Analytics (QMSA) API enables you to access raw resources such as interaction metadata, evaluation results, and users. ## **Authentication** You can try out this API through request authentication using your client credentials. Refer to [Client Credentials](/analytics/docs/how-to-get-api-keys) on the [Getting Started](/tech-partner/docs/getting-started) page for more information. All requests must be made over HTTPS - calls made over HTTP will fail. ## **Regions** The API resources are available distinctly for each geographical region in which the customer is located or provisioned. The base URLs for regions are as follows: URL Region https://api.8x8.com/qm/us-west/v1Western US https://api.8x8.com/qm/us-east/v1Eastern US https://api.8x8.com/qm/uk/v1United Kingdom https://api.8x8.com/qm/ap/v1Australia https://api.8x8.com/qm/ca/v1Canada ## **Headers** With every API call, the header should contain the **`pbx`** name from which the data is to be derived. The header key is **`pbx`**. ## **Resource IDs** The QMSA API uses short non-sequential unique **`ids`**. Every resource **`id`** **must** consist of URL friendly characters such as: * Uppercase or lowercase letters of the alphabet (**`A-Z`** or **`a-z`**) * Numbers (**`0-9`**) * Underscores (**`_`**) or hyphens (**`-`**) The QMSA API uses the following resoruce **`ids`**: * **`userReference`** (e.g., `283`) - The unique identifier of the system registered user. * **`interactionGuid`** (e.g.,`int-15bd0b19d21-KKx2fSQPPTD3DRpOS8UfhmgALh-phone-03-sample`) - The interaction object's globally unique identifier for a single interaction. * **`customField`** (e.g., `customField1`) - One of 25 custom field identifiers. * **`evaluationId`** (e.g., `45`) - The unique identifier for a system evaluation. ## **Representation of Date and Time** All exchange of date and time-related data **must** be completed according to the ISO 8601 standard and stored in UTC. When returning date and time-related data **`YYYY-MM-DDThh:mm:ss`** format **must** be used. ## **Payload Media Type** Where applicable the QMSA API requires the use pf the JSON media-type. Requests that contain a message-body use plain JSON to set or update resource states. `Content-type: application/json` and `Accept: application/json` headers **must** be set on all requests if not stated otherwise. ## **Ordering** By default, all resources returned in collections are ordered by their creation time in ascending order. ## **Pagination** The QMSA API uses URI query pagination to retrieve resource collections. When a resource collection is obtained, the method used to obtain the total count of the type resourced is also returned. The pages **must** be zero (0) based, and the page size **must** be a value between 1 and 100. The default value is 100. The answer can contain links to the either the **`nextPage`** or **`previousPage`**. ## **HATEOAS** The QMSA API uses the Spring HATEOAS model for retrieving links to related resources within responses. ## **Filtering** The QMSA API is designed with limited filtering capiblities. The filtering that is available in each method is possible only for important key/value pairs. More information on what is avaiable for filtering can be found with each method description. ## **Error response** The QMSA API returns both machine-readable error codes and human-readable error messages in the response body when an error occurs. ## **Versioning** This API uses URI versioning. Subsequent versions may introduce breaking changes. ## **Example** Second version of method: `https://api.8x8.com/qm/us-west/V2/method` Security Scheme Type: http HTTP Authorization Scheme: bearer Bearer format: access_token Contact QM/SA Team: [qm-team@8x8.com](mailto:qm-team@8x8.com) Terms of Service {'https://www.8x8.com/terms-and-conditions'} --- ## Remove existing objects. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method enables you to bulk delete object metadata along with associated content. --- ## Ring Group Member Summary(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; **Not currently Testable from here** The Analytics for 8x8 Work Ring Group Memberssummarized performance metrics for individual members of a ring group including Answered and Advanced counts. See [Ring Group Member Summary Glossary](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/ring-group-summary.htm#Glossary-rg-member) for details --- ## (DEPRECATED) Ring Group Summary import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; :::caution deprecated This endpoint has been deprecated and may be replaced or removed in future versions of the API. ::: [Ring Groups Summary](/analytics/reference/ring-group-summary) and [Ring Group Members Summary](/analytics/reference/ring-group-members-summary) should be used. This endpoint is deprecated. --- ## Ring Group Summary(Reference) import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; The Analytics for 8x8 Work Ring Group Summary endpoint enables automated access to summarized call metrics for each ring group. See [Ring Group Summary Glossary](https://docs.8x8.com/8x8WebHelp/8x8analytics-virtual-office/Content/VOA/ring-group-summary.htm#Glossary) for details --- ## Search all buckets. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; A bucket is a resource that is used to store data objects and their associated metadata. This method returns all content within existing buckets. --- ## The list of all objects that meet the filter rule criteria. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; **Tagging** The CSS API features a generic tagging methodology. The tags on each object are determined either systematically or by customer definition. Systematic tags are generated by the underlying provider system. The following object types are supported: * **`callrecording`** * **`callcenterrecording`** * **`screenrecording`** * **`meeting`** The tag types vary depending on the provider. **`callrecording`** * **pbxname** The human readable name of the PBX system. * **extensionId** The machine readable ID of the telephone extension which generated the object. * **pbxId** The machine readable ID of the PBX system. * **callId** The ID which describes the voice interaction. * **direction** The direction of the interaction. * **relativePath** The relative path of the object in storage. * **callerId** The calling line ID generated by the telecom provider. * **startTime** The date/time stamp when the voice interaction was established. * **duration** The duration of the voice interaction in milliseconds. * **extensionNumber** The human readable PBX extension number. * **endTime** The date/time stamp when the voice interaction was terminated. **`callcenterrecording`** * **direction** The direction of the interaction. * **agentId** The agent identifier. * **callId** The call identifier. * **callSnippetId** The identification call for snippets. * **callerId** The caller's phone number. * **calleeId** The called phone number. * **callerName** The caller name. * **calleeName** The called name. * **address** The called phone number. * **queueNumber** The VCC queue number. * **queueName** The VCC queue name. * **channelName** The VCC channel name. * **transactionId** VCC transaction identifier. * **holdDuration** The hold duration. * **billingTelephoneNumber** The phone number used for billing (internal use). * **startTime** The interaction start time. * **duration** The duration of the voice interaction in seconds. * **ipbxid** The PBX name. * **agentName** The agent name. * **tenantId** The tenant identifier. * **extensionNumber** The agent extension number (e.g., 1000). * **mediaUrl** The NFS file name (internal use). * **branchId** The branch (site) identifier. **`screenrecording`** * **agentId** The agent identifier. * **callId** The call identifier. * **startTime** The call start time. * **tenantId** The tenant identifier. * **transactionId** The VCC transaction identifier. **`meeting`** * **duration** The duration of the meeting in milliseconds. * **meetingUrl** The URL which was used for the meeting. * **sessionId** The unique machine readable session ID of the meeting. --- ## Start bulk download. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method starts downloading multiple objects in bulk. The objects are zipped and transferred as one file. --- ## Query supervisor values import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the user's supervisor collection --- ## The Speech Analytics topic count import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the topics count within the specified category --- ## The Speech Analytics topics list import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieve the categories count --- ## Queries a single interaction for matched topics import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the list of detected topics within the transcriptions --- ## Trainer values. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieves the user's trainer collection --- ## This method updates custom fields. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; This method either updates or removes the custom field contained data for user customizable fields. Only custom fields **1** to **15** can be edited. --- ## User details. import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieve user details --- ## Query user count import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieve the total number of users registered in the system. You can also count only the active users. --- ## Query user details import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Retrieve the users collection --- ## Webpage redirect import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint"; import ParamsDetails from "@theme/ParamsDetails"; import RequestSchema from "@theme/RequestSchema"; import StatusCodes from "@theme/StatusCodes"; import OperationTabs from "@theme/OperationTabs"; import TabItem from "@theme/TabItem"; import Heading from "@theme/Heading"; Advanced customers can use this method to integrate systems. 8x8 recommends contacting your account representative for more details. --- ## Work Analytics Customer Data import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; Security Scheme Type: apiKey Header parameter name: 8x8-apikey Security Scheme Type: http HTTP Authorization Scheme: bearer Bearer format: JWT --- ## Work Analytics Historical import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; Security Scheme Type: apiKey Header parameter name: 8x8-apikey Security Scheme Type: http HTTP Authorization Scheme: bearer Bearer format: JWT --- ## Work Analytics import ApiLogo from "@theme/ApiLogo"; import Heading from "@theme/Heading"; import SchemaTabs from "@theme/SchemaTabs"; import TabItem from "@theme/TabItem"; import Export from "@theme/ApiExplorer/Export"; The Work Analytics API provides metrics related to call queues activity as well as agent activity on queues. ### **Time filtering** If no time filtering is provided, the returned metrics reflect the agent activity in the last 24 hours. \ The reports can be generated for a specific date range, specified using 'startDate' and 'endDate' parameters, in ISO 8601 format. If a specific intra day time range is desired, the 'intraDayStart' and 'intraDayEnd', in ISO 8601 format, must be used. ### **Examples:** - To obtain a full day report from 1st of May to 5th of May, in UTC, use startDate: 2023-05-01T00:00:00Z and endDate: 2023-05-05T00:00:00Z. - To obtain a report from 1st of May to 5th of May, in a specific time zone, starting from 8 AM to 6 PM, use startDate: 2023-05-01T00:00:00-0700 and endDate: 2023-05-05T00:00:00-07000, intraDayStart: 08:00:00-0700, intraDayEnd: 18:00:00-0700. Security Scheme Type: apiKey Header parameter name: 8x8-apikey Security Scheme Type: http HTTP Authorization Scheme: bearer Bearer format: JWT --- ## CPaaS: Connect & Communication APIs import Card from '@site/docusaurus/components/Card'; import styles from '@site/docusaurus/components/card.module.css'; 8x8 CPaaS (Communications Platform as a Service) is a comprehensive communication platform that enables businesses to engage with customers through various channels including SMS, Chat Apps, Voice, and Video. Our suite includes Connect (multi-channel portal), Converse (omnichannel customer engagement), Video Interaction, and essential APIs. Explore our complete range of products and services designed to enhance your customer communication experience. ## Communication Channels Engage customers through the channels they already use and love. ## Voice & Video Solutions Advanced voice and video communication tools for enhanced customer engagement. ## Platform Services Essential tools and APIs for building robust communication solutions. ## Management & Analytics Comprehensive tools for managing your communication platform and gaining insights. --- ## Getting Started --- ## Accessing and Viewing Your Customer List The **Partner Hub** in 8x8 Connect is your centralised portal for monitoring your assigned end-customer (Customer) accounts. The interface is designed for view-only access, allowing you to track customer activity without the ability to make changes. 1. **Navigate to the Customer List:** From the main navigation menu on the left, expand the Partner Hub section and click on Customers.. 2. **The 'Manage customers' Page:** This will take you to the "Manage customers" page. Here you will see a list of all customer accounts you have permission to view. This dashboard provides a comprehensive overview, including each customer's: - Customer Name and Account ID - Enabled Products (represented by icons) - Billing Currency - Remaining Credits - Assigned Users (from the partner organization) ![Manage customers](../images/aab83bcb12042396348f186904c8c6f5723fe6e679d85179a891a00fb0140f20-Manage_customers_1.png) 1. **Find a Specific Customer:** Use the **search bar** at the top of the list to quickly find a specific account by their name or Account ID. 2. **View Customer Usage:** To view a specific customer's detailed usage information, simply click on the customer's name in the list. *** ## Understanding the Customer Usage Dashboard After selecting a customer from the "Manage customers" list, you will be taken into their specific account view. The main content area will display an overview, and the navigation menu on the left will update to show the product modules available for that customer. ![view customer non admin role](../images/050fa707fde893e4dee4b59215cea9e8926cc8ea629a0a30f1883874a21a8e31-view-customer_non-admin-role.png) To view the detailed usage for a specific product, simply click on it in the left-hand menu: - **SMS:** View logs for all SMS activity. - **Voice:** View logs for voice calls. - **Video:** View logs for video sessions. - **Messaging Apps:** View logs for messages sent via apps like WhatsApp, Viber, etc. For each log, you can typically see data such as the **Timestamp**, **Direction** (inbound/outbound), **Status** (e.g., delivered, failed), **Price**, and masked `To`/`From` numbers. ### Important Information: Data Access and Privacy As a reminder, all data is presented with security and privacy as a top priority: - **View-Only Access:** You cannot perform any actions or make changes on behalf of the customer. - **Data Masking for Privacy:** By default, sensitive information is automatically masked when viewing usage logs. You will **not** see contact names, message content, or full phone numbers (MSISDN). For identification purposes, only the **last 4 digits** of a phone number will be visible. - **Customer Pricing:** The prices shown will reflect the specific pricing configured for that customer. --- ## Adding Sender ID Details **Adding Sender ID details** * Adding of Sender ID details allows users to define the Sender ID name, the subaccount that they wish to register the Sender ID towards ![image](../images/6cd1498b3aeddbd1353cb7f528423b23f358d464607ef7159f207ab0230e6220-unnamed_5.png) * Our self service allows you to upload multiple sender IDs ![image](../images/0194649400e0f3f4f864ae3ab2bc8c66e21641712f3eefdf4cfe460ae9e3b7e3-unnamed_6.png) ![image](../images/872a00763be6d5bc6d757181343c61365a2ee93d2a09f339f7cbb330e9ae683f-unnamed_7.png) **Sender ID and Brand Name alignment (only for selected countries)** * Adding of Sender ID details, users need to be mindful that for selected countries the brand name (company name) needs to be included in the message content (ie Philippines) and Sender ID needs to linked to the company name. ![image](../images/64d610e279607d21146d74a0d36ce2f245ed626309e92bf77abee739737dd1ac-unnamed_8.png) * If brands are trying to register a SenderID that is not linked to their company/brand name then the user needs to toggle on the `Sender ID does not include company/brand name user needs to upload one of the following documents for the Philippines * IP rights to the Brand Name * Corporate Secretary’s Certificate ![image](../images/9e6971d24e8a12533016ee847097a3d695be35e1633716349d1397e805fe3c93-unnamed_9.png) --- ## Adobe Campaigns [Adobe Campaign](https://www.adobe.com/sea/experience-cloud/topics/campaign-management.html) allows you to launch, measure, and automate campaigns across every channel. Harmonizing all of your marketing channels is not an impossible task. With the help of Adobe Campaign, you can bring customer data from different systems, devices, and channels into a single profile. Then, deliver timely and relevant campaigns that meet your customers in the right places and right ways along their customer journey. With 8x8 cloud communication platform, businesses and developers alike can incorporate SMS functionality into one of their communications channels. ## Configuring SMS Channel To send SMS messages, one or several external accounts must be configured by an administrator under the **Administration > Channels > SMS > SMS accounts** menu. The steps for creating and modifying an external account are detailed in the External accounts section. You will find below the parameters specific to external accounts for sending SMS messages. ## Defining an SMS Routing The external account SMS routing via SMPP is provided by default, but it can be useful to add other accounts. If you want to use the SMPP protocol, you can also create a new external account. For more information on SMS protocol and settings, refer to this [technical note](https://helpx.adobe.com/campaign/kb/sms-connector-protocol-and-settings.html). 1. Create a new external account from Administration > Application settings > External accounts. 2. Define the account type as Routing , the channel as Mobile (SMS) and the delivery mode as Bulk delivery. ![1152](../images/a3d8152-1596705622439.png "1596705622439.png") 3. Define the connection settings. To enter the connection settings specific to sending SMS messages. Please enter the following details: * SMPP connection mode: **Transceiver** * Receiver server: **smpp.8x8.com** * Receiver port: **2776** (TLS v1.3) * Receiver account and password will be provided by your account manager. Please contact [hello-cpaas@8x8.com](mailto:hello-cpaas@8x8.com)) if you have not been allocated someone directly. ![1152](../images/cc24767-1596707708166.png "1596707708166.png") The **Enable TLS over SMPP** option encrypts SMPP traffic using TLS v1.3. Ensure you use port **2776** for your **Receiver port**. **Enable verbose SMPP traces** in the log file allows you to dump all SMPP traffic in log files. This option must be enabled to troubleshoot the connector and to compare with the traffic seen by 8x8. 4. **Contact Adobe** who will give you the value to enter into the SMS-C implementation name field for 8x8l. 5. Define the SMPP channel settings. You can learn more in the [SMS encoding and formats section](https://docs.adobe.com/content/help/en/campaign-standard/using/administrating/configuring-channels/configuring-sms-channel.html#sms-encoding-and-formats). Enable the **Store incoming MO in the database** if you want all incoming SMS to be stored in the inSMS table. For more information on how to retrieve your incoming SMS, refer to this [section](https://docs.adobe.com/content/help/en/campaign-standard/using/communication-channels/sms-messages/managing-incoming-sms.html#storing-incoming-sms). The **Enable Real-time KPI updates during SR processing** option allows the **Delivered or Bounces + Errors KPIs** to be updated in real time after sending your delivery. These KPIs can be found in the **Deployment** window and are directly recalculated from the SR (Status Report) received from 8x8. 6. Define the Throughput and timeouts parameters. You can specify the maximum throughput of outbound messages ("MT", Mobile Terminated) in MT per second. If you enter "0" in the corresponding field, the throughput will be unlimited. The values of all of the fields corresponding to durations need to be completed in seconds. Service type should be "smpp". ![1152](../images/412521e-1596709098471.png "1596709098471.png") 7. Define the SMS-C specific parameters in case you need to define a specific encoding mapping. For more information, refer to the SMSC specifics section. Enable the Send full phone number (send characters other than digits) option if you don't want to respect the SMPP protocol and transfer the + prefix to the server of 8x8 (SMS-C). 8. If needed, define automatic replies to trigger actions based on the content of a reply. For more on this, refer to this section . 9. Save the configuration of the SMS routing external account. You can now use your new routing to send SMS messages with Adobe Campaign. --- ## Analytics 8x8 Connect allows users to monitor their usage, metrics and access logs, throughout the product families: * SMS Analytics * Chat App Analytics * Video Interaction Analytics This following section and it's pages will explain the analytics pages in depth. > 📘 **Voice Analytics** > > The voice analytics section is currently under development, and the corresponding documentation page will be added once it is finalized. > > ## Location of Analytics Sections These sections are located on the sidebar of connect under these tabs: ![image](../images/5a13179-image.png) --- ## API Endpoint migration guide import ApiChip from '@site/docusaurus/components/ApiChip'; # API Endpoint migration guide This outlines the changes to our API endpoints and provides guidance for migrating your applications to use the updated endpoints. We're introducing these changes to improve consistency, reliability, and functionality across our services. These endpoint changes include updates to both **SMS** and **Messaging Apps** services. Please review all changes carefully to ensure a smooth endpoint transition. ## Key Changes - SMS endpoints: Several endpoints have been consolidated for improved efficiency. - Response format standardization across Messaging Apps endpoints. - Parameter handling changes in certain endpoints. ## What To Do - Migrate to the **New Endpoint URL** and corresponding payload/response updates. - Contact support for any clarification. ## SMS API Endpoints > 📘 > > *The SMS endpoint payload and response remain consistent with the previous version and remain unchanged.* > > *Migrate and update your API calls to use the new endpoint URL, as listed in the New Endpoint column.* | Old Endpoint | New Endpoint | |------------------------------------------------------------------------------|---------------------------------------------------------------------------| | | [Send SMS Single](/connect/reference/send-sms-single) | | | [Send Many SMS](/connect/reference/send-many-sms) | | | [Send Many SMS](/connect/reference/send-many-sms) | | | [Cancel Scheduled Message](/connect/reference/cancel-scheduled-message) | | | [Cancel Many SMS Messages](/connect/reference/cancel-many-sms-messages) | ## Messaging Apps API Endpoints > 📘 > > *Migrate to the new endpoint (see New Endpoint column).* > > *There are updates in the payload and response structures; refer to the New Endpoint column for documentation to update corresponding payload/response properties.* | Old Endpoint | New Endpoint | Payload/Response Changes | |---------------|--------------|-------------------------| | | [Mark Message Read](/connect/reference/mark-message-read) | Payload Changes:- Payload is removed.- `umid` is passed directly in the path of new endpoint. | | | [Send Message](/connect/reference/send-message) | Response Changes:- `Status` propertyOld Success Response: `"status": {``"code": "QUEUED",``"description": "Message is accepted and queued for processing"``}`New Success Response:`"status": {``"state": "queued",``"timestamp": "2021-01-04T08:19:45.99Z"``}` | | | [Send Message](/connect/reference/send-message) | same, as for | | | [Send Message Many](/connect/reference/send-message-many) | same, as for | | | [Send Message Many](/connect/reference/send-message-many) | same, as for | --- ## API Error codes > ℹ️ **Troubleshooting tip** > > This page documents **platform level** API error codes returned in 8x8 API responses. > > For delivery related errors coming back *after* we hand messages to suppliers, see the [Messaging Apps Delivery Receipt Error Codes](/connect/reference/delivery-error-codes) reference and [SMS Delivery Receipt Error Codes](/connect/reference/delivery-receipts-error-codes) > > ## HTTP Error Codes 8x8 API might return the following HTTP error codes: | Code | Description | |------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | 200 | **OK**, The request has succeeded. | | 201 | **Created**, The request has succeeded and a new resource has been created as a result. | | 202 | **Accepted**, The request has been received but not yet acted upon. | | 204 | **No Content**, The request has succeeded, but there is no content to send as a response. | | 400 | **Bad Request**, Request has invalid syntax. | | 401 | **Unauthorized**, The client must provide the correct API key in the `Authorization` header. | | 403 | **Forbidden**, The client is authenticated but does not have permissions to the content | | 404 | **Not Found**, The server can not find the requested resource. | | 410 | **Gone**, The requested content has been permanently deleted from the server, with no forwarding address. This usually applied to the API version that's not supported anymore. | | 422 | **422 Unprocessable Entity**, The request was well-formed but was unable to be followed due to semantic errors. | | 426 | **Upgrade Required**, The client is using an unsupported TLS version. Upgrade to TLS 1.3 or higher. The response includes an `Upgrade: TLS/1.3` header. | | 429 | **Too Many Requests**, The user has sent too many requests in a given amount of time ("rate limiting"). | | 500 | **Internal Server Error**, The server has encountered a situation it doesn't know how to handle. | All API responses with HTTP code 400 and above has the following properties: * `code` (integer) - Error code * `message` (string) - Human-readable error description * `errorId` (UUID) - Unique id of the error. You can use it as a reference when sending inquiries to 8x8 support. * `timestamp` (string, date-time) - Date and time of the error occurrence. Format: `yyyy-MM-ddTHH:mm:ss.ffZ` Example of error JSON object returned by the API ```json { "code": 1300, "message": "Object wasn't found or is already expired", "errorId": "1cc1eda1-f5dd-ea11-8288-0263195dd35a", "timestamp": "2020-12-22T05:52:01.85Z" } ``` ## API Response Property Values The table below refers to the possible values of the "code" parameter, in the response body returned by the API as shown above. They can be used for additional clarity of what type of error was encountered. #### SMS API, Messaging API | Code | Description | |------|--------------------------------------------------------------------------------------------------| | 1000 | Invalid parameter | | 1001 | Illegal SubAccountId | | 1002 | Invalid MSISDN | | 1003 | Invalid Encoding | | 1004 | Invalid Text | | 1005 | Invalid Source | | 1006 | Invalid Expiry | | 1007 | Invalid SMS Template | | 1008 | Invalid ID | | 1009 | Invalid Country Code | | 1010 | Parameter out of range | | 1011 | Invalid Schedule Time | | 1012 | Invalid Ip Address | | 1014 | Insecure Protocol — unsupported TLS version | | 1100 | Bulk limit reached | | 1200 | Unauthorized Access | | 1201 | Access forbidden | | 1300 | Not found | | 1400 | Resending interval violation | | 2000 | Internal Error | | 2001 | Function not implemented | | 2002 | Unsupported API version | | 2003 | Unsupported product | | 3001 | Missed User | | 3002 | Missed MSISDN | | 3003 | Invalid MSISDN | | 3004 | Missed Content | | 3005 | Missed Text | | 3006 | Too long text | | 3007 | Missed Media URL | | 3008 | Invalid Media URL | | 3009 | Too long ClientMessageId | | 3010 | Invalid Content Type | | 3011 | Too long SMS Source | | 3012 | Missed messages | | 3013 | Too much messages | | 3014 | Too long ClientBatchId | | 3015 | Missed UserId | | 3016 | Missed WeChatUserId | | 3017 | Too long Fallback text | | 3018 | Invalid Country code | | 3019 | Missed FacebookUserId | | 3020 | Invalid DR Callback URL | | 3021 | Empty or Invalid Location | | 3022 | Invalid Fallback Channel | | 3023 | Invalid Fallback Delay range | | 3024 | Invalid Fallback Status | | 3025 | Invalid Fallback Status Delivered | | 3026 | Invalid Fallback Status Read | | 3027 | Invalid Fallback SubAccount Channel | | 3028 | Missed ZaloUserId | | 3029 | Missed UMID | | 3030 | Empty Template | | 3031 | Invalid Template Name Length | | 3032 | Invalid Template Name | | 3033 | Too much template parameters passed | | 3034 | Invalid template components combination | | 3035 | SubAccount don’t have channels, supported templates | | 3036 | Empty Template language | | 3037 | Invalid Template language | | 3038 | Template not found | | 3039 | Invalid Template Component type | | 3040 | Empty template parameters | | 3041 | Invalid Template parameter | | 3042 | Invalid Template parameter type | | 3043 | Template Text parameter length exceeded | | 3044 | Template Location parameter empty | | 3045 | Template parameter not allowed | | 3046 | Template parameter URL is invalid | | 3047 | Missed WhatsApp UserID | | 3048 | Using ChatGroupId with UserId not allowed | | 3049 | Invalid button parameter | | 3050 | Missed KakaoId | | 3051 | Invalid Fallback ChannelID | | 3052 | Invalid Template Component SubType | | 3053 | Invalid index | | 3060 | Template is being deleted. [Learn more](https://www.facebook.com/business/help/2047376461998278) | --- ## API Rate Limiting To protect the platform from being overloaded and maintain a high quality of service to all customers, 8x8 enforces API rate limits for its SMS API. The default request rate limit is **1800** HTTP requests per second **per sub-account** (can be adjusted upon request), and **3000** requests per second **per IP address** with no maximum daily quota. All requests exceeding this quota will be rejected by the API with `429 Too Many Requests` HTTP Status. The API will also return the `Retry-After` HTTP header with the value indicating when the client can retry the request. Retry-After header example ```text Retry-After: 1 ``` In this example, you can retry the request after 1 second. > 👍 **Hint** > > If you need to submit a higher volume of messages in bulk, please ensure to use the [Send SMS batch](/connect/reference/send-many-sms) API that allows you to submit up to **10,000** messages in a single API request, which currently is roughly equivalent to 25 000 messages per second. > > --- ## Apple's Shortcuts Previously known as Workflow, Apple’s new automation app, [Shortcuts](https://support.apple.com/en-au/HT209055), lets users create powerful workflows and automation with simple building blocks that can be triggered with a tap of a button. One of the best things about Shortcuts is its ability to interact with any web API. ## What you'll need * 8x8 Connect Account * 8x8 SMS and Chat Apps Service * The Shortcuts app [from the App Store](https://apple.co/2DlWWv5) ![248](../images/db8175b-Apple_short_1.jpg "Apple short 1.jpg") ## Send Bulk SMS Using Apple’s Shortcuts 1. Open Shortcuts and tap Create Shortcut, or + from the upper-right corner of the screen. ![248](../images/de9e0e7-Apple_short_2_.jpg "Apple short 2 .jpg") ![248](../images/793adf9-Apple_short_3.jpg "Apple short 3.jpg") Then, search for URL, and type in your SMS API endpoint, as shown. Your endpoint can be found in your [API Keys](https://connect.8x8.com/messaging/api-keys), and will be in the following format: `https://sms.8x8.com/api/v1/subaccounts/{subAccountId}/messages` ![248](../images/765ae74-Apple_short_4.jpg "Apple short 4.jpg") ![248](../images/1200905-Apple_Short_5.jpg "Apple Short 5.jpg") 2. Add another action by searching for “Get Contents of URL”. Under Advanced, select POST as your Method. ![248](../images/6ce5b30-Apple_Short_6.jpg "Apple Short 6.jpg") ![248](../images/f520c6a-Apple_Short_7.jpg "Apple Short 7.jpg") ![248](../images/a37d4b2-apple_short_8.jpg "apple short 8.jpg") Add Headers as follows: ```text Authorization | Bearer {api key} Content-type | application/JSON ``` ![248](../images/ca69630-apple_short_9.jpg "apple short 9.jpg") 3. Add a request body by selecting JSON. Fill out the following fields in your request body: ```text Destination – The mobile number you are sending the SMS to Source – Your Sender ID Text – Your text message content ``` ![248](../images/6a96419-Apple_short_10.jpg "Apple short 10.jpg") 4. Now run it by clicking on the play sign on the bottom right. You should receive an SMS (if you use your own mobile phone) and see a JSON response. ![248](../images/4aad756-Apple_short_11.jpg "Apple short 11.jpg") ![248](../images/90d4905-Apple_short_13.jpg "Apple short 13.jpg") ## Sending Bulk SMS Using An Excel File 8x8’s SMS API supports sending SMS for multiple numbers using a .csv or excel file. Using this [API endpoint](/connect/reference/send-many-sms), you will be able to create a Shortcut that will send SMS to multiple numbers. To simplify things for you, we’ve already made the shortcut that you can reuse or modify to your own preference. [Access our shortcut now](https://www.icloud.com/shortcuts/61ac868c82404fcc9f221ac401ebe623). All you have to do is change the following information on the shortcut we’ve shared. For the SMS API endpoint, it should follow the following format: `https://sms.8x8.com/api/v1/subaccounts/{subAccountId}/messages/batch` Your headers should be as follows: ```text Authorization | Bearer {api key} Content-type | application/json ``` To run your shortcut, you need an excel file with the phone numbers stored on your iOS device. Just share the excel file with Shortcuts, we’ll use [this shortcut](https://routinehub.co/shortcut/1192) to parse the excel files before sending the data to the API. Fill in the message body and sender ID that you’d like to send from, and that’s it! --- ## Auth0 ## Auth0 integration ### About Auth0 [Auth0](https://auth0.com) is an identity management platform that let applications developers easily implement authentication and authorisation logic into their applications. Coupled with 8x8 SMS connectivity, it allows developers to send one-time passwords via the Auth0 platform to their users all over the World. ### Integrating Auth0 with 8x8 SMS API To leverage SMS API to send OTP codes to your users, you will be using the hook feature in Auth0. More specifically, you will be using the "Send a phone message" hook and adapt it to send requests to 8x8 SMS API. #### Prerequisites * [Auth0 account](https://auth0.com/signup?place=header&type=button&text=sign%20up) * [8x8 CPaaS subaccount](https://connect.8x8.com/messaging/api-keys) * [8x8 CPaaS apikey](https://connect.8x8.com/messaging/api-keys) #### Steps 1. In your [Auth0 portal](https://manage.auth0.com/dashboard/), go to the Auth pipeline > Hooks section. 2. Select "Create a hook" in the top right corner. 3. You can call it by any name, let's call it "8x8". 4. Select type "Send phone message" and click on "Create". 5. Scroll down on the Hooks page to the "Send phone message" section and click on the pencil/edit button to edit the hook you just created. 6. Click on the wrench icon (settings) and select "Secrets". 7. Select "Add Secret" and input for secret key: `subaccount` and for secret value, your 8x8 SMS subaccount (can be obtained in [8x8 Connect portal - API Keys section](https://connect.8x8.com/messaging/api-keys)). 8.Select "Add Secret" and input for secret key: `apikey` and for secret value, your 8x8 SMS apikey (can be obtained in [8x8 Connect portal - API Keys section](https://connect.8x8.com/messaging/api-keys)). 8. Close the secrets and settings section. 9. Copy and paste this code in the code section of the hook in place of the code already there: ```javascript module.exports = function (recipient, text, context, cb) { const axios = require("axios").default, API_KEY = context.webtask.secrets.apikey, SUBACCOUNT = context.webtask.secrets.subaccount, BASE_URL = "https://sms.8x8.com/api/v1/"; let instance = axios.create({ baseURL: BASE_URL, headers: { "Authorization": `Bearer ${API_KEY}`, "Accept": "application/json", "Content-Type": "application/json" }, }); instance({ method: "post", url: `subaccounts/${SUBACCOUNT}/messages`, data: { "encoding": "AUTO", "destination": recipient, "text": text } }) .then((response) => { cb(null, {}); }) .catch((error) => { cb(error); }); }; ``` 11. Save the code. 12. Click the play button to open the runner. 13. Wait for the logs stream to load. 14. Press the run button (Bottom right) . 15. Check that you have no errors in the hook log stream and the response has a status code of 200. 16. Check 8x8 Connect logs and verify that you Auth0 submitted an SMS to the test number with your account. 17. You're all set! You can now use this hook in your Auth pipeline! #### Video steps --- ## Authentication - API Keys ## Overview 8x8 APIs accepts an **ApiKey Bearer Token** authentication method. * You can generate tokens from your customer portal [8x8 Connect](https://connect.8x8.com/) * You need to include the following header in your requests: `Authorization: Bearer {apiKey}` * *NB: (replace the `{apiKey}` placeholder with the key generated from the customer portal)* If you have not created your account yet, please head to [8x8 Connect sign-up page](https://connect.8x8.com/login/signup) ## API Key Management ### API Key Creation **Step 1:** View the API Keys Section of the Connect Dashboard. This may be accessed [here](https://connect.8x8.com/messaging/api-keys). ![image](../images/611133d-image.png) **Step 2:** Click the "Create API Key" button. ![image](../images/6dc9838-image.png) **Step 3:** Name the API Key in the Pop Up, this can be any value. We recommend using a memorable name related to the API key's intended purpose such as "HealthCareApp\_Production". Once the value is entered in, click **save**. ![image](../images/a5372f9-image.png) **Step 4:** The new API key should now be located in the list, you can perform a partial search at the top for the name. Only the last 6 characters will be shown, click the document button highlighted in red to reveal the entire API key. You can return to this page to retrieve the API key's value at any time. ![image](../images/9f50dec-image.png) ### API Key Delete/Disable You can disable or even delete an API key if needed and create a new one. This may be useful if the API key has been compromised in some way or if you plan to regularly rotate the API key. The **Pen** or the **Trash Can** button in the picture shown below would grant you access to either enable/disable the API key or permanently delete it. ![image](../images/2e77274-image.png) --- ## Automation Builder ## Overview The Automation Builder consists of both an API platform which is covered in it's own section of our documentation and also a UI that is located in 8x8 Connect. The UI allows you to do similar tasks such as creating and managing complex business workflows. ## Video Guide **Note:** The Video Guide contains an older version of the Automation Builder UI. Some new steps have been added since publishing and some changes may have occured. --- ## Billing ## Global Billing | Agent Billing Category | Definition | Details | Common use cases | | :--------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------- | :-------------------------------------------- | | Basic Message | A simple text message sent via RCS.simple text message sent via RCS. | No rich media or interactivity. Usually limited to 160 UTF-8 characters. | Alerts, OTPs, transactional notifications | | Single Message | A one-off message that may include rich media, rich cards, or suggested replies/actions. | Charged per message sent. Can include CTAs, media, or carousels. | Promotions, service updates, marketing bursts | | Conversational | Session-based pricing model. A single charge applies for unlimited two-way messaging within a defined window (typically 24 hours). | Includes rich and basic messages. Encourages real-time customer interactions. | Customer support, lead capture, chat flows | **Non-Conversational Billing Categories** Agents classified under Basic Message or Single Message billing categories are considered non-conversational. These agents are not billed per conversation. Instead, they are charged per message **What Is a Conversation in RBM?** In the context of Conversational billing, a conversation refers to a 24-hour window during which messages are exchanged between a user and a conversational agent. Only agents under the Conversational billing category can generate and be billed for conversations. **Types of Conversations** • A2P (Application-to-Person): Starts when the user replies to a message from the agent. • P2A (Person-to-Application): Starts when the agent replies to a message initiated by the user. **Conversation Window** • A conversation begins when either the agent or the user responds within 24 hours to a message from the other party, and there’s no active conversation. • The conversation remains active for the next 24 hours and includes: • The initial message that triggered the reply • All messages exchanged during that 24-hour period ![diagram](../images/e0304fb6b81fb3f81739894fe3da7e155ee540376571f6089e31c9037cfcb964-Diagram.png) **What Initiates a Billable P2A Conversation?** It is important to understand which user (P2A) interactions are considered billable "messages" that start the 24-hour conversational session. Not all user actions from an RCS message are billable. A conversation is only initiated when the user sends a message back to the agent. Actions like opening a web page or dialing a number do not count as messages and are not billable. The following table clarifies which common user actions will trigger a billable P2A session: | User Action | Considered a P2A Message? | Billable Outcome | | :------------------------------------------------------ | :------------------------ | :-------------------------------------------------------------------------------- | | Sends a freeform text response | Yes | Yes. This message initiates a single, billable 24-hour conversational session. | | Clicks a suggested response/reply | Yes | Yes. This message initiates a single, billable 24-hour conversational session. | | Sends a file (e.g., image, video) | Yes | Yes. This message initiates a single, billable 24-hour conversational session. | | Clicks a suggested action (e.g., Open URL, Dial number) | No | No. This action does not send a message and does not initiate a billable session. | | Shares location via a location push request | No | No. This action does not send a message and does not initiate a billable session. | **Conversation-Based Billing** • For agents with Conversational billing, charges apply per conversation session, not per message. • This pricing model encourages rich, two-way engagement without inflating cost per interaction. **Important Notes ** • Non-conversational agents are billed per message, not per conversation, even if replies occur. • Billing data for conversational agents (e.g., logs and reports) may be delayed by up to 48 hours to ensure all messages in the session are accounted for before billing is finalised. **Important Billing Considerations** The RCS billing models described on this page represent the standard framework. However, the global messaging ecosystem is complex. Specific billing rules, rates, and the implementation of conversational sessions can sometimes vary depending on the destination country and the recipient's mobile network operator (MNO). 8x8 works to standardise these models for our customers, but underlying carrier policies can occasionally affect the final billing treatment. For the most accurate and detailed billing information applicable to your specific use cases and target regions, we strongly recommend that you speak with your 8x8 Account Manager or contact our sales team. They can provide precise details and help you forecast your messaging spend effectively. ## US Billing | Agent Billing Category | Definition | Details | Common use cases | | --- | --- | --- |----------------------| | Rich Message | A message that contains only text and a limited set of interactive actions. | • Bills in segments of 160 UTF-8 bytes. • Contains only text. No rich media is supported. • Can contain suggested replies. • Can contain the following suggested actions: ◦ Dial phone ◦ Open URL (without Webview) Note: A rich card containing only a title/description is not considered a text-only rich message and would be classified as a Rich Media Message.\" | Alerts, OTPs, transactional notifications, simple service prompts.| | Rich Media Message | A message that message that contains multimedia, text, and a full range of interactive actions. | • Charged per message sent. • Contains multimedia or text. • Media assets can be up to 100 MB. • Can contain all suggested replies and suggested actions (e.g., Open URL with Webview, Show location, Request location). Note: A message is automatically classified as a Rich Media Message if it contains any multimedia. A text-only message is also classified as a Rich Media Message if it includes suggested actions not supported by the Rich Message tier (e.g., Open URL with Webview).\" | Promotions, service updates, marketing bursts, rich transactional receipts, interactive product carousels. | --- ## Braze [Braze](https://www.braze.com/) is a comprehensive customer engagement platform that powers relevant and memorable experiences between consumers and the brands they love. With 8x8’s cloud communication platform, businesses and developers alike can incorporate SMS functionality into their user engagement strategies. ## Some use cases * Send a personalized SMS message to potential customers triggered from an app or web * Send SMS notifications to customers based on app or web event ## Product scope * Braze products ## What you'll need * A 8x8 customer Portal account * Your Braze account ## Creating a Braze webhook 1. Head to [8x8 Connect](https://connect.8x8.com) to get your API Key and Subaccount Id 2. Navigate to the webhook template editor in Braze Portal 3. Create a new template in Braze Portal 4. Save your template in Braze Portal Get your subaccount id and API key from [8x8 Connect](https://connect.8x8.com). Enter your email address and password to get access to your account dashboard. Head over to the **side menu > API keys** section. Create an API key if empty and then keep the API Key value. ![1684](../images/1c05451-connect.8x8.com-2022.07.08.png "connect.8x8.com-2022.07.08.png") After retrieving the Sub-Account ID and API key from 8x8 connect, navigate to your Braze account, and under **Engagement** click **Templates & Media** and select the **Webhook Templates** tab to create a new webhook template. From there, select **Blank Template** to set up a new webhook. ![1508](../images/47663f7-template.png "template.png") Add `https://sms.8x8.com/api/v1/subaccounts/{subAccountId}/messages` under **WEBHOOK URL**, where **subAccountID** is the Sub-Account ID from your API Keys page which can be found in the **API Keys** menu. **REQUEST BODY** should be the default option which is **JSON Key/Value Pairs**. Now add three new pairs in any order named **source, destination, and text**. The source should be the sender ID which is the name or number you will see when you receive an SMS, the destination is the mobile number in an international format where you are sending the SMS and text is the body message of your SMS. After it’s successfully set up, it should look similar to the image below: ![1463](../images/f5c02f5-braze_template_.png "braze template .png") Now, from the​ **Settings**​ tab, add two new request headers named **Authorization**​, which has the value **Bearer** and **Content-Type** which has the value **application/json**. ![1027](../images/c3b4540-bearer_braze.png "bearer braze.png") Finally, to perform a test on the webhook, navigate to the ​**Test**​ tab and click on the **Send Test** button. ![1023](../images/b2031aa-test_braze.png "test braze.png") If everything is set up properly, a successful webhook response (200) message should appear as shown below. ![1662](../images/131ab48-Success.png "Success.png") Your test sample message should look like this on the receiver's mobile phone. ![1242](../images/e4e4288-IMG_0473.jpg "IMG_0473.jpg") After a successful test, click the **Save Template** button and your webhook will be established for future use within your app. ![1038](../images/c407d36-save_template.png "save template.png") --- ## Messaging Apps Fallback management The 8x8 Messaging API enables you to define a fallback sequence, orchestrating message delivery across multiple channels such as WhatsApp, Viber, and SMS. Fallback configurations can be set at the subaccount level (contact your account manager for assistance) or specified per message via the [Messaging Apps Send API](/connect/reference/chatapps-send-api). > 🚧 **Note** > > Fallback sequences defined in the Send API override any existing subaccount-level settings. *** ## Configuring a Fallback Sequence To define a fallback sequence, include the `channels` array in your message payload. Each channel object can specify: - **`channel`**: The messaging channel (e.g., `WhatsApp`, `Viber`, `SMS`). - **`fallbackAfter`** *(optional)*: Time in seconds to wait before triggering the next channel. - **`successStatus`** *(optional)*: The message status considered as successful delivery (`Accepted`, `Sent`, `Delivered`, `Read`). Example: ```json { "channels": [ { "channel": "WhatsApp", "fallbackAfter": 60, "successStatus": "Read" }, { "channel": "Viber", "fallbackAfter": 60, "successStatus": "Delivered" }, { "channel": "SMS" } ], "user": { "msisdn": "+65000000" }, "type": "text", "content": { "text": "Hello World!", "sms": { "encoding": "AUTO", "source": "SENDERID" } } } ``` In this configuration: 1. The message is first sent via **WhatsApp**. If not **read** within 60 seconds, it falls back to: 2. **Viber**. If not **delivered** within 60 seconds, it finally falls back to: 3. **SMS**. *** ## Message Status Reference The `successStatus` parameter determines which message status is considered a successful delivery, preventing fallback to the next channel. Possible values include: - `Accepted`: Message accepted by 8x8's platform. - `Sent`: Message sent to the operator; acknowledgment pending. - `Delivered`: Message delivered to the recipient. - `Read`: Message read by the recipient. For detailed status definitions, refer to the [Message Status Reference](/connect/reference/message-status-references). *** ## Conditional Fallback Based on Message Delivery To attempt delivery via WhatsApp and fallback to SMS only if the message is not delivered within a specific timeframe, configure as follows: ```json { "channels": [ { "channel": "WhatsApp", "fallbackAfter": 60, "successStatus": "Delivered" }, { "channel": "SMS" } ], "user": { "msisdn": "+65000000" }, "type": "text", "content": { "text": "Hello World!", "sms": { "encoding": "AUTO", "source": "SENDERID" } } } ``` In this setup: 1. The message is sent via **WhatsApp**. 2. If the message is not **delivered** within 60 seconds, it falls back to **SMS**. This configuration ensures that the fallback to SMS occurs only if the WhatsApp message isn't delivered within the specified timeframe. *** ## Single-Channel Messaging If you intend to send a message exclusively through a single channel without any fallback options, you can specify only that channel in the `channels` array. In such cases, the `fallbackAfter` and `successStatus` parameters are **optional** and typically **unnecessary**, as there are no subsequent channels to fallback to. Example: ```json { "channels": [ { "channel": "WhatsApp" } ], "user": { "msisdn": "+65000000" }, "type": "text", "content": { "text": "Hello World!", "sms": { "encoding": "AUTO", "source": "SENDERID" } } } ``` In this configuration, the message is sent solely via **WhatsApp**, with no fallback to other channels. *** ## Aligning Fallback Timing with WhatsApp Template TTL When using WhatsApp Utility or Authentication templates, it's crucial to configure the template's Time-To-Live (TTL) appropriately. Setting a TTL ensures that WhatsApp will attempt to deliver the message within a specified timeframe. To prevent delivering duplicate messages, ensure that your fallback duration (`fallbackAfter`) exceeds the template's TTL. This strategy allows WhatsApp to attempt delivery within its validity period before triggering fallback channels like SMS. For detailed guidance on configuring TTL, refer to the [WhatsApp Template Validity Period (TTL) Guide](/connect/docs/guide-whatsapp-template-validity-period-ttl). ### **Example: Coordinating WhatsApp Template Validity with SMS Fallback Timing** Consider a scenario where you send a delivery notification via a WhatsApp Utility template with a validity of 10 minutes (600 seconds). To prevent duplicate messages, set the `fallbackAfter` duration to exceed the WhatsApp template validity. For instance, setting `fallbackAfter` to 900 seconds ensures that the fallback to SMS occurs only after WhatsApp's delivery window has expired. Here's a simplified flow diagram illustrating this process: ![image](../images/94d6af03e9b289df4b5ff226567a0f0531ab415307923da0181e7b67895fa836-image.png) In this flow: - **Start**: Initiate the process. - **Send WhatsApp Utility Template**: Dispatch the message via WhatsApp with a TTL of 10 minutes. - **Delivered within TTL?**: Check if the message was delivered within the TTL. - **Yes**: If delivered, end the process. - **No**: If not delivered, proceed to wait until TTL expires. - **Wait until TTL expires**: Hold until the TTL period concludes. - **Trigger SMS Fallback**: Send the message via SMS as a fallback. - **End**: Conclude the process. This setup ensures that the fallback to SMS occurs only after the WhatsApp message's validity period has expired *** > ❗️ **Important** > > Ensure that your fallback settings align with the channels you intend to use and that all necessary configurations (e.g., sender IDs, templates) are properly set up for each channel. > For a comprehensive list of supported messaging channels, refer to [Supported Messaging Apps](/connect/reference/list-of-supported-chatapps-channels). --- ## CleverTap - SMS Integration Clevertap is a Mobile Marketing Platform with app marketing automation helping app marketers to retain user engagement. CleverTap supports any SMS provider via an HTTP integration. The SMS provider should support receiving messages via the HTTP protocol. ## Some use cases * Send an SMS or Chat Apps message marketing offers from an event being tracked. * Send an SMS or Chat Apps message notifications to customers triggered from the mobile or web app. ## Product scope * Clevertap ## What you'll need * 8x8 SMS or Chat Apps * Clevertap (paid or trial) ## Video Guide This video serves as a companion to this documentation page. ## Setup Clevertap's SMS generic integration In the CleverTap Dashboard, navigate to **Settings > Engage > Channels > SMS** ![image](../images/9e087ea-image.png) ## Setup In the **Setup** Tab, enter the following: **Provider:** Other (Generic) **Nickname**: Any value is fine **Callback URL**: Default **Request Type:** POST **HTTP Endpoint:** [https://sms.8x8.com/api/v1/subaccounts/{subAccountId}/messages/batch](https://sms.8x8.com/api/v1/subaccounts/%7BsubAccountId%7D/messages/batch) Replace **`{subAccountId}`** above with the subaccountID you would like to use. You can find your subAccountID in your 8x8 [Customer Portal](https://connect.8x8.com) under API Keys. ![image](../images/5af06c8-image.png) ## Authentication Navigate to the Authentication section and fill in the following field. **Type:** No Authentication ![image](../images/27528c3-image.png) ## Headers For headers, fill in the following key value pairs. **Authorization:** Bearer You can find or generate your API Keys in your 8x8 [Customer Portal](https://connect.8x8.com) under API Keys. **Content-Type:** application/json ![image](../images/cc9b53d-image.png) ## Parameters Fill in the following values for Parameters **Type:** JSON **Input Box:** ```json { "source": "", "destination": "$$To", "text": "$$Body", "mid": "$$MessageID", "encoding": "Auto" } ``` You will need to use a Sender ID or Virtual Number that is registered to your 8x8 account in the source value. **Batch:** Unchecked ![image](../images/af0a5ab-image.png) ## Other Parameters These can be left unchecked/checked depending on your desired settings in Clevertap. **Custom Key-value pairs in campaigns**: Unchecked or Checked, depending on your preference. **Mark as default:** Unchecked or Checked, depending on your preference. ![image](../images/94ec801-image.png) ## Testing SMS and Saving Channel Settings Before leaving, you can send a test SMS using the Send test SMS link at the bottom of the page. ![Send SMS Test](../images/538c55d-image.png) ![Successful Confirmation Dialog](../images/247285f-image.png) ![SMS Received on Mobile](../images/2458f78-image.png) Once the test is successful you can hit save to use 8x8 as an SMS Provider. --- ## Clevertap - WhatsApp Integration ## Overview Clevertap is a Mobile Marketing Platform with app marketing automation helping app marketers to retain user engagement. CleverTap supports WhatsApp Business API integration. ## Some use cases * Send a WhatsApp message containing marketing offers from an event being tracked. * Send a WhatsApp message to customers triggered from the mobile or web app. ## Product scope * Clevertap ## What you'll need * 8x8 Account * WhatsApp Business Account (WABA) with 8x8 * Clevertap (paid or trial) ## Setup ### Setup WhatsApp Connect Provider In the CleverTap Dashboard, navigate to **Settings > Engage > Channels > WhatsApp > WhatsApp Connect Tab** ![image](../images/e085d19-image.png) Click on the **Provider Configuration** button to set up a new provider. In the Setup Tab, enter the following values: | Field | Value | | --- | --- | | Provider | Other (Generic) | | Nickname | Any Value | | Delivery Report Callback URL | Leave as Default, Copy value to send to 8x8 | | Inbound Message Callback URL | Leave as Default, Copy value to send to 8x8 | | Request Type | POST | | HTTP End Point | [https://chatapps.8x8.com/api/v1/subaccounts/{{subaccountid}}/partners/clevertap/wa](https://chatapps.8x8.com/api/v1/subaccounts/%7B%7Bsubaccountid%7D%7D/partners/clevertap/wa) | After entering the values, ensure that you copy the value for the **Delivery Report Callback URL** and the **Inbound Message Callback URL** and send an email to [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) with a request to enable the Clevertap integration for WhatsApp for your 8x8 account. ![Provider Details](../images/9121be5-image.png) ![Request Body and Headers](../images/78598fc-image.png) After inputting the values above, click **Send Test WhatsApp** to test the integration by sending a WhatsApp message. Follow the directions in the dialog box to send the test WhatsApp message. This WhatsApp message should be sent to your test WhatsApp Number which can be your personal WhatsApp Account for example. ![image](../images/e3b50b48ef3b2a45cfd8fe83db12220e8bfd9ff0e6eba865ef50f51b006de666-image.png) If the message is sent successfully, you should see the following dialog: ![image](../images/b66aa37-image.png) As well as a corresponding WhatsApp Message sent to your WhatsApp Account. ![image](../images/df00240a3c28a189f881d98f0d665308ad2b1e88bfe737abf8829298ca174c3e-image.png) ## Using Campaigns To use this new WhatsApp Provider in campaigns, select **WhatsApp** as a Messaging Channel when you create a new campaign. ![image](../images/3a39d4e-image.png) In the next screen you should see the new WhatsApp Provider available as an option ![image](../images/31546a3-image.png) Afterwards you can proceed to send WhatsApp messages in the campaign. For further details on how to send a campaign, please refer to [Clevertap's guide](https://docs.clevertap.com/docs/intro-to-campaigns). ## Templates In order to send Template messages you will need to register your 8x8 WhatsApp Templates on Clevertap. Please see [Clevertap's guide](https://docs.clevertap.com/docs/generic-whatsapp#adding-message-template) for further details on how to add templates for a Generic WhatsApp Provider. --- ## CleverTap Clevertap is a Mobile Marketing Platform with app marketing automation helping app marketers to retain user engagement. Please see our pages in this section for details on our integrations: * [SMS](clevertap-sms-integration) * [WhatsApp](clevertap-whatsapp-integration) --- ## Cognigy Cognigy is a premier Conversational AI platform that empowers organizations to build and deploy advanced virtual agents, streamlining customer interactions across various channels without the need for deep technical expertise. Among its robust capabilities, Cognigy Extensions stand out by enabling seamless integration with external services and systems, such as the 8x8, to enhance conversational experiences with functionalities like sending SMS messages directly from the conversational interface This document will outline the pre-requisites, and how to install and use 8x8 Send SMS node ## Pre-requisites * Cognigy subscription * 8x8 Connect account ## Installing the 8x8 Extension 1. Login to [Cognigy](https://app.cognigy.ai/) 2. On the left menu, click on **Manage**, then **Extensions** 3. In the Marketplace section, select 8x8 and on the right panel that pops up, select **Install** ![image](../images/34b84d1-Screenshot_2024-03-20_at_12.44.33_PM.png) ## Node: Send SMS This node enables you to send outbound SMS messages to enhance customer engagement and deliver timely notifications. Configure it with your 8x8 Connect credentials, define message content, and specify recipient numbers for personalized communication. ### Important requirement This Extension needs a Connection to be defined and passed to the Nodes. The Connection must have the following keys: * API Key * key: **apiKey** * value: Your 8x8-Connect API Key * Subaccount Id * key: **subAccountId** * value Your Subaccount Id ### Setup Steps 1. In your flow, click the (+) icon to add a node. Select Extensions, scroll to the right and select **8x8** 2. In the dropdown list, select **Send SMS** ![image](../images/3890d4e-image.png) 3. Click on the Send SMS node. 4. In the Edit Node section on the right, click on the (+) icon under **8x8 SMS Connection**. 5. Set up the node connection with apiKey and subAccountId from your [8x8 Connect](https://connect.8x8.com/) account. **Optional**: Set up the Sender ID in the **Source** field if you have a Sender ID depending on your country's regulations. Reach out to [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) if you need help. This Flow Node sends an SMS message to a provided destination, while the result is stored in the `input` or `context` object: ```json { "umid": "158ebe36-14f6-4b51-8121-b099006d829a", "clientMessageId": null, "destination": "+6512345678", "encoding": "GSM7", "status": { "code": "QUEUED", "description": "SMS is accepted and queued for processing" } } ``` ### Exit Points The node will confirm successful message dispatch or failure, allowing for appropriate flow actions. * SMS Success * SMS Error --- ## Company Details Tab **Accessing Company Details Tab** * Add company details ahead of time by selecting `Company details` on the Documents & details module * Alternatively, you can access the `Company details` module via URL: [https://connect.8x8.com/messaging/sender-id/documents-details?tab=sender-id-company-details](https://connect.8x8.com/messaging/sender-id/documents-details?tab=sender-id-company-details) ![image](../images/adbd4aa31a619f3500b0572ace4ae0f3e46dcce2af29999166150ca36643625d-123.png) **Adding Company Details** * Select Destination country name (Currently Indonesia, Philippines, Singapore and Thailand are offered) * Select the Headquarters (this depends if your company has a local entity in the country). For some countries, headquarters is not a required field * For selected countries, Industry is a required field (this depends on the industry of your company) ![image](../images/b578fa2bc67672c882ae69f5a649a9895fe9864739e4bdb0197d1e2577174ccf-1234.png) * Fill up relevant details * Click Save details when done * It is mandatory to include the company letter hand (sample of a letterhead is shown and is also reflected on how letter head is used in LOA generation) ![image](../images/ef29a9813cbb411a8cfaf05c02d9f03b921ecebb831041b0b5335a1859fd43fa-12345.png) --- ## Overview(Docs) ![1280](../images/cf1de9e-New_Project_8.png "New Project (8).png") Introducing 8x8 Connect. A multi-channel communication platform designed for businesses. 8x8 Connect is an intuitive, all-in-one multi-channel communications management portal. It allows you to manage your SMS, Chat Apps, Video Interaction and Voice campaigns across multiple channels, send millions of messages through a single platform, and get real-time reports for better analytics and optimization. --- ## Security (SSO) ## Single Sign-On (SSO) This assumes that you already have an SSO application that will be used to configure your SSO. If you do not have one yet, you might want to check these popular SSO services like OKTA and OneLogin Only users with “admin” access are allowed to configure SSO. If you do not have “admin” access, please contact your system administrator or any user from your account that has “admin” access to the customer portal (8x8 Connect). SSO is only available to enterprise customers. If you want to check your account, please contact our support team [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) or get in touch with your account manager. ### Steps 1. Login to the customer portal with an admin role 2. Click the upper-right gear icon and select “User management” ![user management](../images/16dee2b-user_management.png) 3. Once you are inside the user management page, click the “Configure Single Sign-On” button. ![image](../images/5550d7f-image.png) 4. An overlay SSO configuration page will appear where you will need to fill different information needed. ![image](../images/c69c76a-image.png) 5. Login to your SSO application that you are using and go to your identity provider SAML settings. Copy the url we’ve generated for you and paste it into the Single Sign-On URL ![As an example, here I pasted the value under OKTA SAML settings](../images/b27d35a-image.png)As an example, here I pasted the value under OKTA SAML settings 6. Next copy the identity provider url which is basically your SAML endpoint from your SSO application. Paste it on the “Identity Provider URL” input field. ![image](../images/c8582f3-image.png) 7. Next copy the provider issuer id or “entity id” from your SSO application and paste it on the “Identity Provider Issuer” input field. ![image](../images/a7ca0ec-image.png) Most SSO applications generate and provide these information. On OKTA they are provided by clicking Identity provider metadata ![OKTA Provider Metadata](../images/f1bf01d-image.png)OKTA Provider Metadata ![Result after clicking metadata](../images/0f1e4e9-image.png)Result after clicking metadata The url itself is your **Identity provider URL** while an XML key called **entityID** is your **Identity Provider issuer** 8. Most SSO apps provide x509 certificates, just copy the contents of this certificate which looks something like this image below and paste it on the “Key x509 certificate” text area field. ![image](../images/47302e9-image.png) 9. Once everything has been filled up, click “Save” ![image](../images/70fcf0c-image.png) 10. Log out of the customer portal and now try to log in using SSO. ![image](../images/89a5a75-screenshot-connect.8x8.com-2024.02.27-10_31_37.png) ![image](../images/f90b6cf-screenshot-connect.8x8.com-2024.02.27-10_32_32.png) **Notes when logging in using SSO:** By default, all users without “admin”(administrator) access will be forced to login via SSO once it is configured. Forl users with “admin” access, they can choose to use the normal login using a username/password combination or via SSO. --- ## Contact Management(Docs) Contact Management is a feature inside Connect that allows you to manage your contacts in bulk whether you will use them to send messages or for Converse(converse.8x8.com). You can also use this feature to group or blacklist your contacts. ## Video Guide We have a video guide below to accompany this documentation page. ## Creating contacts There are two(2) ways to add your contacts, either you bulk upload or enter. **Steps in creating a single contact** *(This assumes you have access to contacts management and that you are already logged in)* 1. On the left navigation menu click "Contacts" and you should be redirected to the Contact Management page as shown below ![contacts management](../images/f1091c7-contacts-management-1.png "contacts-management-1.png") 2. Click "Create Contact" and a pop-up form will appear where you will be able to enter all the information about your contact. You can start with the basic user information, channel information and any additional information that you might want to add for your contact. ![contacts create](../images/1fba4d9-contacts-create-A.png "contacts-create-A.png") The channels tab ![contacts create](../images/0feb00d-contacts-create-B.png "contacts-create-B.png") Additional information tab (You can add as many information as you want) ![contacts create](../images/1d29481-contacts-create-C.png "contacts-create-C.png") 3. Just click on "Create contact" to save. That's it, you just created your new contact. **Create bulk contacts by uploading a file** Our bulk upload method only supports mobile number, first name and last name fields. 1. Start bulk upload, go to Upload contacts tab. ![upload contacts](../images/ea1bda3-Upload-contacts-1.png "Upload-contacts-1.png") 2. Drag the file on the space provided or click the link that says "click to upload". We recommend that you use a .csv file to make sure all your contacts are uploaded. 3. Tag the column where your mobile number is present. ![upload contacts](../images/f47da3a-Upload-contacts-2.png "Upload-contacts-2.png") 4. You have an option to add your contacts to an existing group. If that group doesn't exist, you can easily type the name and the group will be created automatically once you start processing your file. ![image](../images/8891b45-Screenshot_2022-09-20_at_5.07.53_PM.png "Screenshot 2022-09-20 at 5.07.53 PM.png") 5. Once everything is done, then you can just click "Start processing". The status of your upload is shown below if it is still in progress or completed. ![image](../images/0d25daf-Screenshot_2022-09-20_at_5.09.43_PM.png "Screenshot 2022-09-20 at 5.09.43 PM.png") ## Creating groups You can easily group your contacts by creating a new group. Once a group is created you can easily tag your contacts for that group you just created. 1. Click "Contact Groups" tab and then click "Create group" ![Create group](../images/88e8ca9-Create-group.png "Create-group.png") 2. Enter the group name on the field provided. As an option you can enter the description for that group. ![Create group](../images/6d6e4d0-Create-group-A.png "Create-group-A.png") 3. Click "Save" and that's it, you're done creating your group. ## Blacklisting your contacts Blacklisting is useful when you want to avoid sending messages to some of your customers. Usual use-case for this is customer subscriptions where some of your customers want to unsubscribe to your marketing messages. 1. Start by selecting a number of contacts from your contacts list. ![image](../images/37a56dc-Screenshot_2023-06-02_at_9.56.58_AM.png) 2. Scroll to the top and click "Action items", select "Add to Groups" ![image](../images/93b2290-Screenshot_2023-06-02_at_10.03.01_AM.png) 3. You will be prompted to select a group by typing the name or create a new group. Click "Confirm". ![image](../images/e6b06c6-Screenshot_2023-06-02_at_10.07.28_AM.png) 4. Go to "Contact Groups" tab and edit the group you just selected or added. Mark "Blacklisted" by clicking it and then save. ![image](../images/459d796-Screenshot_2022-09-20_at_5.23.28_PM.png "Screenshot 2022-09-20 at 5.23.28 PM.png")That's it you just blacklisted a number of contacts. If you have existing contacts that you want to blacklist on a certain group, simply edit that group and mark them as "Blacklisted". --- ## Context & Scripting > 🚧 **[BETA]** > > This product is currently in early access. Please reach out to your account manager to get more information. > ## Context Workflow context captures the scope in which a workflow is run. Workflow context persists data between steps that you can read from or write to at runtime. Workflow context exposes two state variables **data** and **step. data** is where all workflow-level data is stored. **step** is where all the step-level data is stored. When the step has completed execution, step data is automatically cleared. So, if you want to capture data from a step to use at a later time, you need to save it to **data**. You can interact with the workflow context using the inputs and outputs in the step definitions. Suppose you have a property in a step A called **status** which you want to use in a branch step later on. In order for the branch step to access this value, you need to output the value from step A to workflow context like ```json { "outputs": { "custom_status": "{{step.status}}" } } ``` Now, your workflow **data** has a property named **custom_status** that you access from any other step like ```json { "selectNextStep": { "branchA": "{{data.custom_status == 'OK'}}", "branchB": null } } ``` **data** can also store complex objects. For example, a workflow triggered by an outbound message may have nested properties like ```json { "payload": { "status": { "code": 1001, "errors": ["first error", "second error"] } } } ``` and you can access this code in your workflow context by using **data.payload.status.code** and first error by using **data.payload.status.errors[0]**. ## Scripting Automation service has rich support for scripting with JavaScript (supports most features of ECMAScript 2023) via input and output pipelines in the workflow context like member access operators (**data.member**, **data['test']['key']**), binary and tertiary operators like **x == y ? 1 : 0;** and the following functions we have defined for you. - Check country code of a phone number using **isCountryCode(phoneNumber, countryCode)**. For example, **isCountryCode('+6512345678', 'SG')** evaluates to **true**. - Check if text contains a substring using **stringContains(source,value,ignoreCase)** For example, **stringContains("hello, world","wor",true)** evaluates to **true**. The **ignoreCase** param will ignore case-sensitivity is set to true. If **ignoreCase** is not set then the default is **false** which means it will be case-sensitive. - Check if a timestamp falls within some time of the day for the specified time zone using **isTimeOfDayBetween(timestamp, timeFrom, timeTo, timezone)**. For example, **isTimeOfDayBetween('2020-10-15T14:40:15+07:00', ‘09:00:00', ‘18:00:00’, 'Singapore Standard Time’) ** evaluates to **true**. Refer to Time Zones resource for supported timezones. To check if a timestamp falls outside a specific time interval you can either specify it as two time intervals chained with an** \|\| **(logical OR) condition or a negation** ! **(logical NOT) on a single time interval. For example, **!isTimeOfDayBetween('2020-10-15T14:40:15+07:00', ‘09:00:00', ‘18:00:00’, 'Singapore Standard Time’)** is logically equivalent to**isTimeOfDayBetween('2020-10-15T14:40:15+07:00', ‘00:00:00', ‘08:59:59’, 'Singapore Standard Time’) || isTimeOfDayBetween('2020-10-15T14:40:15+07:00', ‘18:00:01', ‘23:59:59’, 'Singapore Standard Time’) ** - Check the day of the week of a date using **isDayOfWeek(date, day)**. For example, **isDayOfWeek('2021-05-25', 'Tuesday')** evaluates to true. The date can be a simple date or a full datetime in the ISO8601 format. Supported days are Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday. The days are not case sensitive. Any of Monday, monday, MONDAY is acceptable. Scripts must be enclosed in double curly brackets like **{{data.msisdn == 6500000000}}**. The preceding statement will access the `msisdn` property from workflow context at runtime and check that it equals to 6500000000. Scripts for populating strings can be defined like **"{{'umid: ' + data.umid}}"** or **"umid: {{data.umid}}"**. Following restrictions are placed on scripts for security reasons: - Maximum length of scripts is 1000 characters. - Assignments, allocations and type declarations are forbidden. - Recursive functions are forbidden. - Scripts that take more than 500 milliseconds to execute are forbidden (configurable). --- ## Create a group and add a contact to that group > 👍 > > Please see [Create a group](/connect/reference/create-group) for the full API reference > > [Create a group](/connect/reference/create-group) To create a group you need to submit a JSON object to the URL POST **`https://contacts.8x8.com/accounts/{accountId}/groups`** Request body should look like this: ```json { "isBlacklist": false, "name": "Group 1", "description": "Team that belongs to group 1" } ``` If successful you will get a response similar to this: ```json { "id": 8334, "contacts": 0, "createdAt": "2022-05-13T07:03:08.54Z", "name": "Group 1", "description": "Group 1 team", "isBlacklist": false } ``` Otherwise you will get a 400 or a 409 error response. Assuming you've successfully created the group, to add contacts you need to submit a JSON object to the URL POST **`https://contacts.8x8.com/api/v1/accounts/accountId/groups/{groupId}/contacts`** Groupid is the id of the group you just created. For this example **8334** is the group id. When you send a request, it should be an array of contact id like the one below: ```json { "contacts":[ 41702128,41702329 ] } ``` You can use [Get contact information by id](/connect/reference/get-contact-by-id) to get the id of a contact or [Search contacts](/connect/reference/contact-search) where the list shows the id of each contact. Response: Returns 201 Created with location header if the request was successful. If the request failed, an error object will be returned as 404. --- ## Creating a contact > 👍 > > Please see [Create contact](/connect/reference/create-contact) for the full API reference > > To create a single contact you need to submit a JSON object to the URL POST `https://contacts.8x8.com/accounts/{accountId}/contacts` Request body should look like this: ```json { "firstName": "Chathuranga", "lastName": "Pathirana", "externalId": "externalSystemId", "country": "SG", "groups": [ { "id": 72 }, { "id": 82 } ], "addresses": { "msisdn": "6580000000", "weChatUserId": "oJQxo6XXXXXXXXXXXXXXXXX", "facebookUserId": "19520000000000", "email": "user@example.com" }, "customAttributes": { "company": "Google", "jobTitle": "CEO" } } ``` Response: Returns 201 Created with location header if the request was successful. If the request failed, an error object will be returned. --- ## Messaging Apps Delivery Receipt Error Codes > ℹ️ **Troubleshooting tip** > > The table below lists *delivery receipt* error codes for Messaging Apps > If you are debugging an error that appears in the JSON body of an 8x8 **API response** (for example `"code": 3038`), please see the [API Error Codes](/connect/reference/api-error-codes) reference instead. > Possible error codes returned in the Messaging Apps delivery receipts include: ## General Error Codes | Code | Possible reason | Description | |:-----|:-----------------------------------------------|:---------------------------------------------------------------------------------------------------------| | 1 | AbsentSubscriber | Subscriber is not registered in this chat channel | | 2 | ContentRelatedError | Content Type is not supported by this channel | | 3 | DataMissing | The request is missing a required parameter | | 9 | EquipmentProtocolError | The receiver's app version is not capable of receiving business messages | | 11 | Flooding | Too many messages sent to the recipient in a short period of time | | 14 | InternalError | Internal error | | 15 | InvalidDestination | UserId is not valid for that channel or is part of blacklist on Connect | | 18 | Invalid parameter | Invalid or missing parameters. Check that all required parameters are passed and are of the correct type | | 23 | ConnectionError | Channel connection error | | 25 | Operation Aborted By Receiving Network Or User | Message is intentionally undelivered by Channel | | 29 | PhoneRelatedError | The specified parameter value is invalid | | 36 | Expired | Message expired (not delivered at the requested time) | | 41 | SmscReject | Message rejected by Chat channel | | 42 | NoCredit | Not enough credit on Account wallet | | 43 | SpamFilter | Message filtered by anti-spam reason | | 46 | SubscriberNotReachable | Message sent to Channel, but user is not reachable for delivery | | 57 | UnknownError | An unknown error occurred with no more specific reason available | | 61 | SessionExpired | Message trashed by session expired reason | ## WhatsApp Error Codes | Code | Possible Reasons | Description | |:-----|:---------------------------|:--------------------------------------------------------------------------------------------------| | 1000 | Authentication failed | AuthException — Unable to authenticate app user (WhatsApp: 0) | | 1001 | Method not allowed | API Method — Capability/permissions issue (WhatsApp: 3) | | 1002 | Rate limit exceeded | API Too Many Calls — App rate limit reached (WhatsApp: 4) | | 1003 | Permission missing | Permission Denied — Permission not granted or removed (WhatsApp: 10) | | 1004 | Invalid value | Parameter value not valid — Business phone number deleted (WhatsApp: 33) | | 1005 | Invalid parameter | Invalid parameter — Misspelled or unsupported parameter (WhatsApp: 100) | | 1006 | Token expired | Access token expired (WhatsApp: 190) | | 1007 | Policy violation | Temporarily blocked for policy violations (WhatsApp: 368) | | 1008 | Rate limit hit | WABA rate limit reached (WhatsApp: 80007) | | 1009 | Throughput exceeded | Rate limit hit — Message throughput limit reached (WhatsApp: 130429) | | 1010 | Experimental number | Number part of experiment (WhatsApp: 130472) | | 1011 | Region restriction | Business account restricted in this country (WhatsApp: 130497) | | 1012 | Unknown failure | Something went wrong — Unknown error (WhatsApp: 131000) | | 1013 | Access denied | Access denied — Permission not granted (WhatsApp: 131005) | | 1014 | Missing parameter | Required parameter is missing (WhatsApp: 131008) | | 1015 | Invalid parameter value | Parameter value is not valid (WhatsApp: 131009) | | 1016 | Service down | Service unavailable — Temporary service downtime (WhatsApp: 131016) | | 1017 | Sender \= Receiver | Recipient cannot be sender (WhatsApp: 131021) | | 1018 | Message rejected | Message undeliverable (WhatsApp: 131026) | | 1019 | Account locked | Account locked (WhatsApp: 131031) | | 1020 | Display name not approved | Display name approval needed (WhatsApp: 131037) | | 1021 | Bad certificate | Incorrect certificate (WhatsApp: 131045) | | 1022 | Message window expired | Re-engagement message (outside 24-hour window) (WhatsApp: 131047) | | 1023 | Spam control triggered | Spam rate limit hit (WhatsApp: 131048) | | 1024 | Message suppressed | Meta chose not to deliver (WhatsApp: 131049) | | 1025 | Unsupported message | Unsupported message type (WhatsApp: 131051) | | 1026 | Download failed | Media download error (WhatsApp: 131052) | | 1027 | Upload failed | Media upload error (WhatsApp: 131053) | | 1028 | Sender-recipient throttled | Sender/recipient pair rate limit hit (WhatsApp: 131056) | | 1029 | Account under maintenance | Account in maintenance mode (WhatsApp: 131057) | | 1030 | Wrong number of params | Template param count mismatch (WhatsApp: 132000) | | 1031 | Template missing | Template does not exist (WhatsApp: 132001) | | 1032 | Text too long | Template hydrated text too long (WhatsApp: 132005) | | 1033 | Template violation | Template policy violation (WhatsApp: 132007) | | 1034 | Wrong param format | Template param format mismatch (WhatsApp: 132012) | | 1035 | Template paused | Template paused (WhatsApp: 132015) | | 1036 | Template disabled | Template disabled (WhatsApp: 132016) | | 1037 | Flow blocked | Flow blocked (WhatsApp: 132068) | | 1038 | Flow throttled | Flow throttled (WhatsApp: 132069) | | 1039 | Deregistration failed | Incomplete deregistration (WhatsApp: 133000) | | 1040 | Server unavailable | Server temporarily unavailable (WhatsApp: 133004) | | 1041 | PIN mismatch | Two-step PIN mismatch (WhatsApp: 133005) | | 1042 | Reverification required | Phone number re-verification needed (WhatsApp: 133006) | | 1043 | Too many guesses | Too many two-step PIN guesses (WhatsApp: 133008) | | 1044 | PIN entry too fast | Two-step PIN guessed too fast (WhatsApp: 133009) | | 1045 | Number not registered | Phone number not registered (WhatsApp: 133010) | | 1046 | Retry after delay | Wait before registering phone number (WhatsApp: 133015) | | 1047 | Too many attempts | Account register/deregister limit exceeded (WhatsApp: 133016) | | 1048 | Unknown client-side issue | Generic user error (WhatsApp: 135000) | | 1049 | Too many sync calls | Synchronisation request limit exceeded (WhatsApp: 2593107) | | 1050 | Sync time expired | Synchronisation request outside allowed time window (WhatsApp: 2593108) | | 1051 | Message can't be delivered | The recipient has opted-out of receiving marketing messages from your business (WhatsApp: 131050) | | 1052 | Permanent permission already exists | Call permission request cannot be sent because a permanent permission has already been approved by the user for this business account phone number. (WhatsApp: 138017) | | 1053 | Call permission limit reached | Call Permission Request limit reached for this business–consumer pair. Further requests are temporarily blocked until a connected call occurs or the rate limit resets. (WhatsApp: 138009) | | 1054 | BSUID Not Supported | Business-scoped User ID (BSUID) recipients are not supported for this message. | ## Viber Error Codes | Code | Possible Reasons | Description | |:-----|:-----------------------------------|:--------------------------------------------------------------------------------------------------------------| | 2000 | Successfully sent | Message sent successfully | | 2001 | Internal server error | Internal processing failure | | 2002 | Invalid service ID | Service ID unused or not yet uploaded | | 2003 | Bad request structure | Malformed request (e.g. JSON formatting) | | 2004 | Incorrect message type | Unsupported or invalid message type | | 2005 | Missing parameters | Required field like tracking\_data is missing | | 2006 | Timeout | Viber server timeout | | 2007 | User blocked | User has blocked this ID or all business messages | | 2008 | Not a Viber user | Destination number not registered with Viber | | 2009 | No suitable device | Device not compatible with Business Messages | | 2010 | Unauthorized IP or ID | Wrong IP or ID not whitelisted | | 2012 | Bad label | Missing or invalid 'label' parameter | | 2013 | Invalid TTL | TTL is out of allowed range | | 2014 | Session message limit reached | Exceeded 10-message session cap | | 2015 | Unsupported file format | File type not allowed for this feature | | 2016 | Filename too long | File name exceeds 25 character limit | | 2017 | Thumbnail too long | Thumbnail URL exceeds 1000 characters | | 2018 | File too large | File size exceeds 200 MB | | 2019 | Video too long | Video duration exceeds 600 seconds | | 2020 | Template ID not found | The provided template ID is not found | | 2021 | Template validation failed | Template variables did not pass the server validation | | 2022 | Incompatible with Version | Version is not compatible to the message fields | | 2023 | Invalid destination number | The destination number is invalid or does not exist. Please verify the destination phone number and try again | | 2024 | Invalid List Message Parameter | Message delivery failed because one or more List Message parameters are invalid | | 2025 | Invalid Carousel Message Parameter | Message delivery failed because one or more Carousel Message parameters are invalid | --- ## SMS Delivery Receipt Error Codes > ℹ️ **Troubleshooting tip** > > The table below lists *delivery receipt* error codes for SMS > If you are debugging an error that appears in the JSON body of an 8x8 **API response** (for example `"code": 1200`), please see the [API Error Codes](/connect/reference/api-error-codes) reference instead. > > Subject to enhanced details available on from outbound route, the following error codes can be sent by 8x8 in the delivery reports | Code | Reason | | :--- | :--------------------------------------------- | | 0 | No reason code | | 1 | Absent subscriber | | 2 | Content related error | | 3 | Data missing | | 4 | Deferred delivery | | 5 | Pending upstream | | 7 | Delivery failure | | 8 | Deny | | 9 | Equipment protocol error | | 10 | ESME external error | | 11 | Flooding | | 12 | HLR error | | 13 | Illegal subscriber or equipment | | 14 | Internal error | | 15 | Invalid destination | | 16 | Invalid format | | 17 | Invalid message length | | 18 | Invalid parameter | | 19 | Invalid source address | | 20 | Local cancel | | 21 | Memory capacity exceeded | | 22 | Message being retried | | 23 | Network failure | | 24 | Age verification failure | | 25 | Operation aborted by receiving network or user | | 26 | Operation barred | | 27 | Permanent operator error | | 28 | Permanent phone error | | 29 | Phone related error | | 30 | Portability error | | 31 | Premium SMS error | | 32 | Roaming subscriber | | 33 | Route error | | 34 | Screening error | | 35 | Service center congestion | | 36 | SMS expired | | 37 | SMS facility not supported | | 38 | SMS malformed | | 39 | SMSC cancel | | 40 | SMSC error | | 41 | SMSC reject | | 42 | Source credit insufficiency | | 43 | Spam filter | | 44 | Subscriber billing issue | | 45 | Subscriber busy for SMS | | 46 | Subscriber not reachable | | 47 | Subscriber temporary unavailable | | 48 | System failure | | 49 | TCAP error | | 50 | Throttling error | | 51 | Time out error | | 52 | Unable to decode the response | | 53 | Unexpected data value | | 54 | Unexpected error | | 55 | Unidentified subscriber | | 56 | Unknown delivery state | | 57 | Unknown error | | 58 | Unknown service center | | 59 | Unknown subscriber | | 60 | Content filtered | | 61 | Session expired | --- ## Delivery receipts for Outbound Messaging Apps import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; **Delivery Report (DR)** are webhooks for Messaging Apps delivery statuses: `POST` requests sent by the 8x8 platform in `JSON` format to the callback URL configured for your account. ### Requirements To use 8x8 Outbound Messaging Apps capabilities, you need: - An account configured to use Messaging Apps product. - A webhook to indicate to us which URL 8x8 platform should send delivery reports to. > 📘 > > You can configure your callback using [Webhooks Configuration API](/connect/reference/add-webhooks-1) > ### Retry logic In case of connection error/timeout or HTTP response code 4XX or 5XX, there will be multiple retry attempts with progressive intervals: 1, 10, 30, 90 sec. ### Read Receipt - Validity Period If the chat app doesn't provide a read receipt promptly, either because the user hasn't read the message or due to a chat app service issue, we'll continue checking for up to **10 days**. If the user reads the message after this period, the read receipt won't be updated. ### Webhook format > 📘 > > If you are still receiving webhooks in an older format, see the [Webhook migration guide](/connect/docs/webhook-migration-guide) for help migrating your configuration. > Request body description | Parameter name | Parameter type | Description | | --- | --- | --- | | version | integer | **New in v9.** Version of the webhook payload format. Equals to `9` for this format. | | namespace | string | A generic namespace for incoming webhook.Equal to `ChatApps` for delivery receipts. | | eventType | string | Webhook type.- `outbound_message_status_changed` for delivery receipts- `external_app_message` for WhatsApp Business App messages | | description | string | Human-readable description of the incoming event | | payload | object | Delivery receipt information, see below | Payload object description | Parameter name | Parameter type | Description | | :-------------- | :------------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------| | umid | uuid | A unique identifier generated by 8x8 for the message | | batchId | uuid | A unique identifier generated by 8x8 for the message if sent using [Batch API](/connect/reference/send-sms-batch) | | clientMessageId | string | Custom identifier you provided for this message | | clientBatchId | string | Custom identifier you provided for this batch of messages | | subAccountId | string | The sub-account id used to deliver the message | | timestamp | string | UTC date and time when the message was sent expressed in ISO 8601 format.Only present when `eventType=external_app_message` | | channel | string | Name of the channel used to send the message, please see [List of supported Messaging Apps channels](/connect/docs/list-of-supported-chatapps-channels) for details | | user | object | Information about the user the message is associated with | | type | string | Message type. See [Inbound Messaging Apps message type field](/connect/docs/inbound-chatapps-message#webhook-format) for possible values.Only present when `eventType=external_app_message` | | content | object | Message content. Structure varies based on the `type` field.Only present when `eventType=external_app_message` | | status | object | Current status of the message, please see [Message status reference](/connect/reference/message-status-references) for details.Only present when `eventType=outbound_message_status_changed` | | whatsapp | object | WhatsApp-specific information. Only present when `channel` is `whatsapp`. See below for details | | outboundContent | object | **New in v9.** A structured copy of the original outbound message this receipt refers to. WhatsApp only, and only present on the first receipt for the message (the `queued` status). See [outboundContent object](#outboundcontent-object) below. | User information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------- | | msisdn | string | **Changed in v9.** The recipient phone number expressed in E.164 international format. Left out for channels where users have no phone number (e.g. LINE). | | channelUserId | string | **Changed in v9.** The user's id on the channel: the **BSUID** (business-scoped user id) for WhatsApp, or the **Line user id** for Line. Only included when the channel provides a real user id. | When a `user` field has no value, it is simply left out of the JSON. > ⚠️ **When `channelUserId` is missing (WhatsApp only)** > > `channelUserId` is only present in the receipts reported by WhatsApp itself. When the message is sent with a phone number as the destination, the `queued` and `delivered_to_operator` receipts — generated before the message reaches WhatsApp — will **not** contain `channelUserId`, while the `delivered_to_recipient` and `read` receipts **will** include it. > WhatsApp object description | Parameter name | Parameter type | Description | | :---------------- | :------------- | :------------------------------------------------------------------------------------------------------------------------ | | providerErrorCode | string | WhatsApp's own error code. Only present if there was an error | | pricingCategory | string | WhatsApp's pricing category as defined by Meta. Only included with sent status, and one of either delivered or read status | | templateId | string | Template ID used by WhatsApp to deliver the message. Only present for messages sent via the WhatsApp Direct Send API | > 🚧 > > Please note that unlike Delivery Receipts for the SMS API, the Price object is not sent for Messaging Apps Webhooks. > > ❗️ > > If the request you receive has a different structure from described in this document, please contact our support to activate the latest format for your account. > #### outboundContent object `outboundContent` is a structured, channel-normalized copy of the message you originally sent. It lets you reconcile a delivery receipt with the exact content that was delivered without keeping your own copy of the outbound payload. > 📘 > > `outboundContent` is **WhatsApp only**. It is attached to the first receipt for the message, when `state` is `queued`, and is omitted from all subsequent receipts (`delivered_to_operator`, `delivered_to_recipient`, `read`, etc). When the content cannot be reconstructed it is left out entirely. > | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------- | | channel | string | Channel the content was sent on. Always `whatsapp`. | | contentType | string | Kind of content. See [contentType values](#contenttype-values) below. | | template | object | Template metadata. Only present when `contentType` is `template`, `template_auth`, or `carousel`. | | header | object | Message header (text, media, or location). Omitted when the message has no header, and for `carousel` (per-card headers are used instead). | | body | object | Message body text. | | footer | object | Message footer text. | | actions | object | Interactive elements (buttons, call-to-action, list, or flow). Omitted for `carousel` (per-card actions are used instead). | | cards | array | Carousel cards, in order. Only present when `contentType` is `carousel`. | | meta | object | Extra flags such as redaction status and interactive sub-type. | ##### contentType values | Value | Description | | :-------------- | :---------- | | `text` | Freeform text message. | | `media` | Freeform image, video, audio, or document. The media type is carried in `header.type`. | | `location` | Freeform location message. | | `template` | Standard (non-authentication) template message. | | `template_auth` | Authentication template. Its one-time code is redacted — see [Redaction](#redaction) below. | | `carousel` | Carousel template. Content is carried per card in the `cards` array. | | `interactive` | Interactive message (quick-reply buttons, list, call-to-action URL, or flow). The sub-type is in `meta.interactiveType`. | ##### header object | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------- | | type | string | One of `text`, `image`, `video`, `audio`, `document`, or `location`. | | text | string | Header text. Only present when `type` is `text`. | | media | object | Media descriptor. Only present for `image`, `video`, `audio`, and `document` headers. Contains `url`, and `filename` for documents. | | location | object | Location descriptor (`latitude`, `longitude`, and optionally `name` and `address`). Only present when `type` is `location`. | ##### body / footer objects | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------- | | text | string | The body or footer text. | ##### actions object | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------- | | buttons | array | Buttons, in order. Each has `type` (`url`, `quick_reply`, `copy_code`, `phone`, or `flow`), `label`, and `value` (`null` for buttons that carry no value, such as `quick_reply`). | | cta | object | Call-to-action URL button (`displayText`, `url`). | | list | object | List picker: `buttonLabel` plus `sections`, each with an optional `title` and a `rows` array (`id`, `title`, optional `description`). | | flow | object | WhatsApp Flow details (`flowId`, `cta`, `action`, `screen`). | ##### cards array (carousel) Each entry has an `index` (zero-based position) and its own `header`, `body`, and `actions`, structured exactly as above. ##### meta object | Parameter name | Parameter type | Description | | :-------------- | :------------- | :---------- | | redacted | boolean | `true` when part of the content was masked. | | redactionReason | string | Why the content was masked. | | interactiveType | string | Sub-type of an interactive message: `button`, `list`, `cta_url`, or `flow`. | ##### Redaction Some content is masked before it reaches your webhook, in which case `meta.redacted` is `true`: - **Authentication templates** (`contentType` is `template_auth`): the one-time code is replaced with `******` in the body and in any copy-code button. `meta.redactionReason` is `OTP codes are masked in agent-facing contexts`. - **PII masking** (when enabled on your sub-account): the content is withheld, leaving only `channel`, `contentType`, and `meta`. `meta.redactionReason` is `Content masked for PII compliance`. #### Sample payloads ##### WhatsApp — queued Sent when the message is accepted by the 8x8 platform. In this example the message was sent using the phone number (`msisdn`); since this status does not come back from WhatsApp, `channelUserId` is not included. This is also the receipt that carries the `outboundContent` object — a structured copy of the message that was sent. The examples below show the receipt for different content types; only the `outboundContent` object varies, the surrounding webhook body is the same in each case. ```json title="Outbound delivery receipt webhook body (v9) — WhatsApp, queued, freeform text" { "version": 9, "namespace": "ChatApps", "eventType": "outbound_message_status_changed", "description": "ChatApps outbound message delivery receipt", "payload": { "umid": , "subAccountId": , "channel": "whatsapp", "user": { "msisdn": }, "status": { "state": "queued", "timestamp": "2026-07-03T03:23:21.90Z" }, "outboundContent": { "channel": "whatsapp", "contentType": "text", "body": { "text": "Welcome to 8x8 Inc.! We are the leading global provider of unified cloud communications, video collaboration, and contact center solutions." } } } } ``` Template with an image header, a URL button, and a quick-reply button: ```json title="Outbound delivery receipt webhook body (v9) — WhatsApp, queued, template" { "version": 9, "namespace": "ChatApps", "eventType": "outbound_message_status_changed", "description": "ChatApps outbound message delivery receipt", "payload": { "umid": , "subAccountId": , "channel": "whatsapp", "user": { "msisdn": }, "status": { "state": "queued", "timestamp": "2026-07-03T03:23:21.90Z" }, "outboundContent": { "channel": "whatsapp", "contentType": "template", "template": { "name": "marketing_welcome_template", "language": "en_US", "category": "MARKETING" }, "header": { "type": "image", "media": { "url": "" } }, "body": { "text": "Hi Jason, ready to elevate your customer communications? Discover the power of 8x8 CPaaS." }, "footer": { "text": "Powered by 8x8" }, "actions": { "buttons": [ { "type": "url", "label": "Discover all channels", "value": "https://cpaas.8x8.com/en/products/omnichannel-messaging" }, { "type": "quick_reply", "label": "Talk to Sales", "value": null } ] } } } } ``` Interactive list message (note `meta.interactiveType`): ```json title="Outbound delivery receipt webhook body (v9) — WhatsApp, queued, interactive list" { "version": 9, "namespace": "ChatApps", "eventType": "outbound_message_status_changed", "description": "ChatApps outbound message delivery receipt", "payload": { "umid": , "subAccountId": , "channel": "whatsapp", "user": { "msisdn": }, "status": { "state": "queued", "timestamp": "2026-07-03T03:23:21.90Z" }, "outboundContent": { "channel": "whatsapp", "contentType": "interactive", "header": { "type": "text", "text": "8x8 Customer Success personalized sessions" }, "body": { "text": "Looking for personalized assistance? Our Customer Success team has the following slots available. Tap to select a time." }, "footer": { "text": "For urgent inquiries, email cpaas-sales@8x8.com" }, "actions": { "list": { "buttonLabel": "Book Slot", "sections": [ { "title": "Oct 9, 2024", "rows": [ { "id": "slot-1", "title": "Monday, Oct 9", "description": "9:00 AM - 10:00 AM" }, { "id": "slot-2", "title": "Monday, Oct 9", "description": "2:00 PM - 3:00 PM" } ] } ] } }, "meta": { "interactiveType": "list" } } } } ``` ##### WhatsApp — delivered to recipient This receipt comes back from WhatsApp, so the `user` object includes the new `channelUserId` (BSUID). It does **not** carry `outboundContent` — that object is only present on the earlier `queued` receipt. ```json title="Outbound delivery receipt webhook body (v9) — WhatsApp, delivered to recipient" { "version": 9, "namespace": "ChatApps", "eventType": "outbound_message_status_changed", "description": "ChatApps outbound message delivery receipt", "payload": { "umid": , "batchId": , "clientMessageId": , "clientBatchId": , "subAccountId": , "channel": "whatsapp", "user": { "msisdn": , "channelUserId": }, "status": { "state": "delivered", "detail": "delivered_to_recipient", "timestamp": "2026-07-03T03:40:54.16Z" }, "whatsapp": { "pricingCategory": "marketing", "templateId": "1281032340757288" } } } ``` ##### WhatsApp — read recipient This receipt also comes back from WhatsApp, so the `user` object includes `channelUserId`. ```json title="Outbound delivery receipt webhook body (v9) — WhatsApp, read" { "version": 9, "namespace": "ChatApps", "eventType": "outbound_message_status_changed", "description": "ChatApps outbound message delivery receipt", "payload": { "umid": , "batchId": , "clientMessageId": , "clientBatchId": , "subAccountId": , "channel": "whatsapp", "user": { "msisdn": , "channelUserId": }, "status": { "state": "read", "timestamp": "2026-07-03T05:12:41.30Z" }, "whatsapp": { "pricingCategory": "marketing", "templateId": "1281032340757288" } } } ``` ##### Viber — delivered to recipient For Viber and RCS there is no `channelUserId`: these channels identify the recipient purely by phone number, so only `user.msisdn` is sent. ```json title="Outbound delivery receipt webhook body (v9) — Viber, delivered" { "version": 9, "namespace": "ChatApps", "eventType": "outbound_message_status_changed", "description": "ChatApps outbound message delivery receipt", "payload": { "umid": , "batchId": , "clientMessageId": , "clientBatchId": , "subAccountId": , "channel": "viber", "user": { "msisdn": }, "status": { "state": "delivered", "detail": "delivered_to_operator", "timestamp": "2026-07-03T03:21:26.06Z" } } } ``` ##### RCS — read receipt ```json title="Outbound delivery receipt webhook body (v9) — RCS, read" { "version": 9, "namespace": "ChatApps", "eventType": "outbound_message_status_changed", "description": "ChatApps outbound message delivery receipt", "payload": { "umid": , "batchId": , "clientMessageId": , "clientBatchId": , "subAccountId": , "channel": "rcs", "user": { "msisdn": }, "status": { "state": "read", "timestamp": "2026-07-03T03:41:51.85Z" } } } ``` #### WhatsApp Business App Messages When using WhatsApp with Embedded Signup, messages sent by your business through the WhatsApp Business App are forwarded to your configured webhook as `external_app_message` events. This allows you to track all outbound messages sent on behalf of your business through the WhatsApp Business App. > 📘 > > For more information, see [WhatsApp's Embedded Signup documentation](https://developers.facebook.com/documentation/business-messaging/whatsapp/embedded-signup/onboarding-business-app-users#smb_message_echoes). **Key differences from delivery receipts:** - `eventType` is `external_app_message` instead of `outbound_message_status_changed` - Includes `timestamp`, `type`, and `content` fields in the payload (see webhook format above) - Does **not** include `status`, `batchId`, `clientMessageId`, or `clientBatchId` fields ##### Sample WhatsApp Business App message webhook ```json title="WhatsApp Business App message webhook body (v9)" { "version": 9, "namespace": "ChatApps", "eventType": "external_app_message", "description": "External App Message", "payload": { "umid": , "subAccountId": , "timestamp": "2026-01-28T09:16:53.00Z", "channel": "whatsapp", "user": { "msisdn": , "channelUserId": }, "type": "Text", "content": { "text": "Here's the info you requested! https://www.meta.com/quest/quest-3/" } } } ``` Request body description | Parameter name | Parameter type | Description | | --- | --- | --- | | namespace | string | A generic namespace for incoming webhook.Equal to `ChatApps` for delivery receipts. | | eventType | string | Webhook type.- `outbound_message_status_changed` for delivery receipts- `external_app_message` for WhatsApp Business App messages | | description | string | Human-readable description of the incoming event | | payload | object | Delivery receipt information, see below | Payload object description | Parameter name | Parameter type | Description | | :-------------- | :------------- |:--------------------------------------------------------------------------------------------------------------------------------------------------------------------| | umid | uuid | A unique identifier generated by 8x8 for the message | | batchId | uuid | A unique identifier generated by 8x8 for the message if sent using [Batch API](/connect/reference/send-sms-batch) | | clientMessageId | string | Custom identifier you provided for this message | | clientBatchId | string | Custom identifier you provided for this batch of messages | | subAccountId | string | The sub-account id used to deliver the message | | timestamp | string | UTC date and time when the message was sent expressed in ISO 8601 format.Only present when `eventType=external_app_message` | | channel | string | Name of the channel used to send the message, please see [List of supported Messaging Apps channels](/connect/docs/list-of-supported-chatapps-channels) for details | | user | object | Information about the user the message is associated with | | type | string | Message type. See [Inbound Messaging Apps message type field](/connect/docs/inbound-chatapps-message#webhook-format) for possible values.Only present when `eventType=external_app_message` | | content | object | Message content. Structure varies based on the `type` field.Only present when `eventType=external_app_message` | | status | object | Current status of the message, please see [Message status reference](/connect/reference/message-status-references) for details.Only present when `eventType=outbound_message_status_changed` | | whatsapp | object | WhatsApp-specific information. Only present when `channel` is `whatsapp`. See below for details | User information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :--------------------------------------------------- | | msisdn | string | Phone number expressed in E.164 international format | | channelUserId | string | Id of the user in the channel. Always the phone number digits (same value as `msisdn`). | WhatsApp object description | Parameter name | Parameter type | Description | | :---------------- | :------------- | :------------------------------------------------------------------------------------------------------------------------ | | providerErrorCode | string | WhatsApp's own error code. Only present if there was an error | | pricingCategory | string | WhatsApp's pricing category as defined by Meta. Only included with sent status, and one of either delivered or read status | > 🚧 > > Please note that unlike Delivery Receipts for the SMS API, the Price object is not sent for Messaging Apps Webhooks. > > ❗️ > > If the request you receive has a different structure from described in this document, please contact our support to activate the latest format for your account. > #### Sample payloads ##### WhatsApp — delivered to recipient ```json title="Outbound delivery receipt webhook body (v8) — WhatsApp, delivered to recipient" { "namespace": "ChatApps", "eventType": "outbound_message_status_changed", "description": "ChatApps outbound message delivery receipt", "payload": { "umid": , "batchId": , "clientMessageId": , "clientBatchId": , "subAccountId": , "channel": "whatsapp", "user": { "msisdn": , "channelUserId": }, "status": { "state": "delivered", "detail": "delivered_to_recipient", "timestamp": "2025-05-05T09:15:57.00Z" }, "whatsapp": { "pricingCategory": "marketing" } } } ``` ##### WhatsApp — read ```json title="Outbound delivery receipt webhook body (v8) — WhatsApp, read" { "namespace": "ChatApps", "eventType": "outbound_message_status_changed", "description": "ChatApps outbound message delivery receipt", "payload": { "umid": , "batchId": , "clientMessageId": , "clientBatchId": , "subAccountId": , "channel": "whatsapp", "user": { "msisdn": , "channelUserId": }, "status": { "state": "read", "timestamp": "2025-05-17T06:27:52.45Z" }, "whatsapp": { "pricingCategory": "marketing" } } } ``` ##### WhatsApp — undelivered ```json title="Outbound delivery receipt webhook body (v8) — WhatsApp, undelivered" { "namespace": "ChatApps", "eventType": "outbound_message_status_changed", "description": "ChatApps outbound message delivery receipt", "payload": { "umid": , "batchId": , "clientMessageId": , "clientBatchId": , "subAccountId": , "channel": "whatsapp", "user": { "msisdn": , "channelUserId": }, "status": { "state": "undelivered", "detail": "rejected_by_operator", "timestamp": "2026-01-01T00:00:00Z", "errorCode": 15, "errorMessage": "Invalid destination" }, "whatsapp": { "providerErrorCode": "131009" } } } ``` #### WhatsApp Business App Messages When using WhatsApp with Embedded Signup, messages sent by your business through the WhatsApp Business App are forwarded to your configured webhook as `external_app_message` events. This allows you to track all outbound messages sent on behalf of your business through the WhatsApp Business App. > 📘 > > For more information, see [WhatsApp's Embedded Signup documentation](https://developers.facebook.com/documentation/business-messaging/whatsapp/embedded-signup/onboarding-business-app-users#smb_message_echoes). **Key differences from delivery receipts:** - `eventType` is `external_app_message` instead of `outbound_message_status_changed` - Includes `timestamp`, `type`, and `content` fields in the payload (see webhook format above) - Does **not** include `status`, `batchId`, `clientMessageId`, or `clientBatchId` fields ##### Sample WhatsApp Business App message webhook ```json title="WhatsApp Business App message webhook body" { "namespace": "ChatApps", "eventType": "external_app_message", "description": "External App Message", "payload": { "umid": , "subAccountId": , "timestamp": "2026-01-28T09:16:53.00Z", "channel": "whatsapp", "user": { "msisdn": , "channelUserId": }, "type": "Text", "content": { "text": "Here's the info you requested! https://www.meta.com/quest/quest-3/" } } } ``` --- ## Delivery receipts for outbound SMS Delivery Receipts (DR) are webhooks for delivery statuses: `POST` requests sent by 8x8 platform in `JSON` format to the delivery reports callback URL configured for your account. Whenever a message has a new delivery status associated with the delivery stage it is in, 8x8 sends out a `POST` request with the new status to the callback URL. > 📘 > > You can configure your callback using [Webhooks Configuration API](/connect/reference/get-webhooks-2) You can also overwrite the default callback URL on a per-message / per-batch-of-message basis by specifying a different `dlrCallbackUrl` value in your API requests when sending a message or a batch of messages (see [Send API](/connect/reference/send-api-1)) ### Retry logic In case of connection error/timeout or HTTP response code 4XX or 5XX, there will be multiple retry attempts with progressive intervals: 1, 10, 30, 90 sec. ### Delivery Receipt - Validity Period If we do not receive a delivery receipt from the SMS carrier promptly, our platform will continue checking for up to 48 hours for a delivery receipt. If the delivery receipt is received after this 48-hour period, there will be no delivery receipt webhook sent. ### Webhook format Request body description | Parameter name | Parameter type | Description | | --- | --- |--------------------------------------------------------------------------------------| | namespace | string | A generic namespace for incoming webhook.Equal to `SMS` for delivery receipts. | | eventType | string | Webhook type. Equals to `outbound_message_status_changed` for delivery receipts. | | description | string | Human-readable description of the incoming event | | payload | object | Delivery receipt information, see below. | Payload object description | Parameter name | Parameter type | Description | | :-------------- | :------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | umid | uuid | A unique identifier generated by 8x8 for the message | | batchId | uuid | A unique identifier generated by 8x8 for the message if sent using [Batch API](/connect/reference/send-sms-batch) | | clientMessageId | string | Your custom identifier for the message | | clientBatchId | string | Client managed id for this batch of messages : your own unique reference | | subAccountId | string | The sub-account id used to deliver the message | | source | string | The source (i.e: sender) used to deliver the message | | destination | string | Destination phone number where the SMS was sent (E.164 format) | | status | object | Current status of the message, please see [Message status reference](/connect/reference/message-status-references) for details. | | price | object | Price information of the message, this field is optional in the response if there is no charge incurred. Please see [Price object reference](/connect/reference/price-object-reference) for details | | smsCount | integer | Number of SMS segment in the message | > 🚧 > > Please note that the Price object is optional and might not be included in the Delivery Receipts callback. When a message was not sent successfully, you will still receive Delivery Receipts with Failed/Undelivered status without incurring any charges. Hence, the price information is not available for the Delivery Receipts. > > ❗️ > > If the request you receive has a different structure from described in this document, please contact our support to activate the latest format for your account. > ### Sample delivery receipt for outbound SMS ```json { "namespace": "SMS", "eventType": "outbound_message_status_changed", "description": "SMS outbound message delivery receipt", "payload": { "umid": "9e09ac86-bd74-5465-851d-1eb5a5fdbb9a", "batchId": "3e09ac86-bd74-5465-851d-1eb5a5fdbb9b", "clientMessageId": "1e09ac86-bd74-5465-851d-1eb5a5fdbb9b", "clientBatchId": "2e09ac86-bd74-5465-851d-1eb5a5fdbb9b", "subAccountId": "SubAccount-1", "source": "8x8", "destination": "+12025550293", "status": { "state": "undelivered", "detail": "rejected_by_operator", "timestamp": "2016-01-01T00:00:00Z", "errorCode": 15, "errorMessage": "Invalid destination" }, "price": { "total": 0.0375, "perSms": 0.0125, "currency": "USD" }, "smsCount": 3 } } ``` ```xml SMS outbound_message_status_changed SMS outbound message delivery receipt 9e09ac86-bd74-5465-851d-1eb5a5fdbb9a 3e09ac86-bd74-5465-851d-1eb5a5fdbb9b 1e09ac86-bd74-5465-851d-1eb5a5fdbb9b 2e09ac86-bd74-5465-851d-1eb5a5fdbb9b SubAccount-1 8x8 +12025550293 undelivered rejected_by_operator 2016-01-01T00:00:00Z 15 Invalid destination 0.0375 0.0125 USD 3 ``` --- ## Details **Filling Up Details** * Select Destination country name (Currently Indonesia, Philippines, Singapore and Thailand are offered) * Select the Headquarters (this depends if your company has a local entity in the country). For some countries, headquarters is not a required field * For selected countries, Industry is a required field (this depends on the industry of your company) * Once the above fields are filled `Company details` will appear. Here you can can choose to add pre added company details as before by selecting `Select company` or you can choose to `Add company details`. This has been covered in `Company Details` portion ![image](../images/41301da6f40da76418653ce3035dc2d1a6ba16e1e62c0fc92714cdd627eedb17-unnamed_2.png) --- ## Detrack - SMS Integration ## Introduction **Detrack** is a scalable, smart, and easy to use tracking service with automatic real-time notifications when orders are delivered. **Detrack** provides a rule-based email and text / SMS notification feature, that allows users to send email and text messages to customers, company staff, or any other person who needs to receive those messages based on event triggers. With this integration, we can leverage the Detrack event triggers and 8x8 API to send SMS when an event occurs. --- ## Video Demo This video demo will show how you can integrate 8x8's SMS API with Detrack in order to send SMS notifications. --- ### Integrating Detrack notifications with SMS 1. From the Detrack Dashboard, Go to **Settings > Notifications**. ![Navigate to Detrack Notifications](../images/9917b14-image.png) 2. Go to **Text / SMS settings** tab and fill out the following **required** values. | Field | Value | | --- | --- | | Text / SMS Provider | 8x8 (Wavecell) | | Country Code | Country Code of your Sender ID | | Sender ID | Your Sender ID or Virtual Number | | Send test Text / SMS To | The number you would like to send your test notifications to. | | 8x8 (WaveCell) API Key | Your 8x8 API Key. Please generate one from the following [page](/connect/docs/authentication#api-key-management) if you do not have it. | | 8x8 (WaveCell) Sub-account Id | The Subaccount ID containing the Sender ID / Virtual Number you would like to send from. | ![Text / SMS Settings Example](../images/0fee60a-image.png) Text / SMS Settings Example 3. Then go to the **Notification** settings tab for a list of notifications that have been pre-set for you. You can click on any existing notification to edit it or click on the **Add Notification** button to add a new notification (or trigger event). ![Add Notifications](../images/f512399-image.png) The steps to configure a Notification are continued in the following section below. --- ### Configuring your Detrack Notifications 1. From the **Add Notification** form, go to the **Trigger** tab. 2. Select the Detrack **Job Type** i.e Job (Delivery + Collection) / Delivery / Collection. In this example we select a Job. 3. Select the **Events** that will trigger the notification. In this example we select a completed event. 4. Select the **Group** that you wish to filter. If no Group is selected, then notifications are sent for all jobs regardless of Group. 5. If the **Failed** event is selected, you will be able to select a failed Reason to filter. If no Reason is selected, then notifications will be sent for all failed jobs regardless of Reason. 6. If the **Pre-job** event is selected, you will be able to select which day and what time to send notifications. * For the **Pre-job Sending Date** option, you can select up to 5 days prior to the scheduled date to send the notification. * For the **Pre-job Sending Time** option, you can select the time of the selected day (above) to send the notification. ![Trigger](../images/0109a34-image.png) --- ### Customizing text / SMS notifications 1. Go to the **Text / SMS** tab. 2. For the **Send Text / SMS to** option, select the field containing the mobile number that you wish to notify when the event is triggered. This field is **optional** – you can choose not to send any text by not selecting any field. 3. If you wish to notify other recipients such as internal staff or 3rd party contractors, enter their mobile numbers (one per line) in the **Other Fixed Numbers** box provided. This field is **optional** – you can choose not to send any text to others by leaving the field empty. 4. Type your customized text message in the **Text / SMS body** field. 5. A tracking link will be included in your text message after the text body. The link, when tapped on smartphones, will send the recipients to a direct tracking page where they can easily track the status of their deliveries. The recipients will also be able to rate the Goods / Service, the Driver, as well as provide feedback regarding the delivery to you. 6. Click **Save and send a text notification** button to send a sample text notification to the test phone number or click **Save** to save your settings. ![SMS content](../images/5483fc1-image.png) --- ## Developer Tools 8x8 Connect allows developers to manage their API Keys, Webhook endpoints, and IP Whitelisting easily through their respective pages. ## API Keys This [page](https://connect.8x8.com/messaging/api-keys) allows to see, create and manage any API keys associated to your account. API keys are used to authenticate with all 8x8 APIs, more information can be found [here](/connect/docs/authentication). ## Webhooks This [page](https://connect.8x8.com/webhooks) allows you to see, create and manage your webhooks. Webhooks are used for 8x8 to automatically send you information such as delivery receipt and incoming messages. You can find more informations on webhooks [here](/connect/reference/webhook-object-structure) . The webhook list will show you information such as: * **Subaccount:** The subaccount the webhook is attached to. * **Type:** The type of webhook which relates to the API it is tied to (Chat Apps, SMS). * **URL:** The URL the webhooks are being sent. * **HTTP Authorization:** The HTTP Authorization (if any) that is being used. For now the HTTP Authorization is a string that is added to the header that your server can use to authenticate the webhook. * You can find more information on the parameter [here](/connect/reference/add-webhooks-2) under the **Body** Params Object. * ![image](../images/040e6b5-image.png) * **Content Type (Deprecated):** No longer used in newer webhooks. * **Status:** Whether the webhook is enabled/disabled ![image](../images/0802bc480ff22ed58122232f5e40adacf3516f0df00af4d55fe024660840bee5-image.png) When you click on the **Add webhook** button, you will see this pop up. ![image](../images/387b93267655a4331902596fe3a1c8cd6130c015f46db87fb5946f659194290a-image.png) The popup above allows you to create new webhooks, it can be enabled for all sub-accounts or for a specific one only. You can then select if you want to setup this webhook for SMS, Messaging Apps, or Voice. If you select SMS you will receive both incoming SMS and SMS delivery information on this webhook. For Messaging Apps and Voice, you can customize your configuration for each type of webhook. ![image](../images/327f72555df4e12e56400e42f0b0db583adb9748a478f42247b322680128e67a-image.png) ![image](../images/c138b61a28c32ba9e5bb300b2bc76c8823e424f6d262270ec9e7ef78deb41863-image.png) The **HTTP Authorization** field is an **optional** parameter, in case your webhook requires authentication. Webhooks can also be managed via API, [here](/connect/reference/get-webhooks-1) for ChatApps and [here](/connect/reference/get-webhooks-2) for SMS, and [here](/connect/reference/get-webhooks-information) for Voice. ## IP Whitelisting With 8x8, your communication is secure, even in the unlikely event that someone gets hold of your API key. That's because only authorised devices from your approved locations (whitelisted IPs) can access our platform. You can set this up using your **8x8 Connect** portal [here](https://connect.8x8.com/messaging/api-keys). Under **IP Whitelisting** section. You may click the **Create IP Address** button. ![image](../images/360d113f60b072bb26844af5dc219319d95393fd2a5153cd7863cf49f73b203d-image.png) You will be prompted to input the IP Address and SubAccount. If you do not input a SubAccount, the IP Address will be whitelisted for all SubAccounts. Click **SAVE** once done. ![image](../images/8b365c0b471f91febeccd1d7edd8010d180982382345f55be5c11994e50cacb3-image.png) --- ## Documents & Details Dashboard **Accessing Documents & Details Dashboard** * Select `Documents & details` under the Sender ID module on your left hand side menu * Alternatively you can access the Documents& Details Dashboard via URL: [https://connect.8x8.com/messaging/sender-id/documents-details?tab=sender-id-documents-details](https://connect.8x8.com/messaging/sender-id/documents-details?tab=sender-id-documents-details) * Add Documents for a particular country and headquarter before hand so that your documents are updated. Preview Documents that have been uploaded * Download the current page into a CSV **Documents can be added ahead of time** * Country (Indonesia\*, Philippines, Singapore and Thailand) * Headquarter (Local or International) * Industry (where applicable) * Provide a document name **File type and limits** * Accepted file types are .pdf, .jpg and .jpeg * up to 5MB per file ![Document Details](../images/856198abb5faa617f8b7bb627faf70f7c5c933fbbe7ddc02f33d2265cf7fe459-Document__Details_.png) ![Document Details - Indonesia](../images/d9cd8d8774b2e5ecec77729447bda24372ab43909de36c6a40c4e087c37e20b3-Document__Details_-_Indonesia.png) --- ## Adding Documents **Attach Company Documents** * Company documents can be attached ahead of time as covered in the `Documents Tab` section * Each document is provided with a sample document for reference ![image](../images/f9f7b94d2868c652d25ab106b4bf3c5cebf458cf833734d6b27ea9e187f2782b-unnamed_3.png) ![image](../images/1334500e591346f3f3f2f151906f3ffac4ed89873156ac85e277474f4aba0f26-unnamed_4.png) --- ## Download Messaging Apps Logs Messaging Apps Reporting API allows you to retrieve your Messaging Apps activity with individual logs for each message sent/received for a given sub-account. ### Flow 1. Start the reporting job execution and get the job identifier 2. Request the job status using job identifier 3. Download logs once the job is finished. > 👍 > > Logs can be retrieved as far as 6 months in the past from the current date. > > --- ## Drafts Tab **Sender ID Drafts Dashboard** * Allows user to view the drafts (unsubmitted sender ID registrations that were saved as drafts) * View drafts sorted by company, country and on last updated date (default view is by the latest updated draft) * Clicking on the edit button allows user to access the unsubmitted registrations (higlighted in the red box) **Sorting filters** * Country (sort in ascending order [a-z] or in descending order [z-a] of the destination name) * Sender ID (sort in ascending order [a-z] or in descending order [z-a] of the Sender ID name) * Company name (sort in ascending order [a-z] or in descending order [z-a] of the company name) * Last Updated Date sort in ascending order [earliest date - latest date] or in descending order [latest date - earlier date] ![image](../images/d13757f656b51b403abedd876d19866b641a57c5601aa6c1b792ba6a07079a79-Sender_Drafts.png) --- --- ## Examples > 🚧 **[BETA]** > > This product is currently in early access. Please reach out to your account manager to get more information. > ## Examples Chat Apps auto reply: ```json { "subAccountId": "acme_corp_chatapps", "trigger": "inbound_chat_apps", "status": "enabled", "definition": { "name": "Auto Reply ChatApps", "steps": [ { "id": "send_CA", "stepType": "ChatAppsMessage", "inputs": { "subAccountId": "acme_corp_chatapps", "user": { "msisdn": "{{data.payload.user.msisdn}}" }, "type": "text", "content": { "text": "Hello, thank you for your message!" } } } ] } } ``` SMS auto reply: ```json { "subAccountId": "acme_corp", "trigger": "inbound_sms", "status": "enabled", "definition": { "name": "Auto Reply SMS", "steps": [ { "id": "send_sms", "stepType": "SMS", "inputs": { "subAccountId": "acme_corp", "source": "Acme Corp", "destination": "{{data.payload.source}}", "text": "Hello, thank you for your message!", "encoding": "Auto" } } ] } } ``` Out of Office with country branch: ```json { "subAccountId": "acme_corp_chatapps", "trigger": "inbound_chat_apps", "status": "enabled", "definition": { "name": "Out of Office", "steps": [ { "id": "branch_on_msg_country", "stepType": "Branch", "selectNextStep": { "message_from_ID": "{{isCountryCode(data.payload.user.channelUserId, 'ID')}}", "message_from_PH": "{{isCountryCode(data.payload.user.channelUserId, 'PH')}}", "message_from_Others": null } }, { "id": "message_from_ID", "stepType": "If", "inputs": { "condition": "{{!isTimeOfDayBetween(data.payload.timestamp, '09:00:00', '18:00:00', 'SE Asia Standard Time')}}" }, "do": [ [ { "id": "message_from_ID_out_of_business_hours", "stepType": "ChatAppsMessage", "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "user": { "msisdn": "{{data.payload.user.channelUserId}}" }, "type": "text", "content": { "text": "Halo, terima kasih atas pesan Anda! Kami akan menghubungi Anda kembali besok." } } } ] ] }, { "id": "message_from_PH", "stepType": "If", "inputs": { "condition": "{{!isTimeOfDayBetween(data.payload.timestamp, '09:00:00', '18:00:00', 'North Asia East Standard Time')}}" }, "do": [ [ { "id": "message_from_PH_out_of_business_hours", "stepType": "ChatAppsMessage", "inputs": { "subAccountId": "acme_corp_chatapps", "user": { "msisdn": "{{data.payload.user.channelUserId}}" }, "type": "text", "content": { "text": "Kumusta, salamat sa iyong mensahe! Babalikan ka namin bukas. \r\n Hello, thanks for your message! We will get back to you tomorrow." } } } ] ] }, { "id": "message_from_Others", "stepType": "ChatAppsMessage", "inputs": { "subAccountId": "acme_corp_chatapps", "user": { "msisdn": "{{data.payload.user.channelUserId}}" }, "type": "text", "content": { "text": "Hello, thanks for your message! We will get back to you as soon as possible." } } } ] } } ``` Keyword detection: ```json { "subAccountId": "acme_corp", "trigger": "inbound_sms", "status": "enabled", "definition": { "name": "Promo_register", "steps": [ { "id": "keyword", "stepType": "branch", "selectNextStep": { "register_flow_1": "{{stringContains(data.payload.content.text, 'Register')}}", "others": null } }, { "id": "register_flow_1", "stepType": "HttpRequest", "inputs": { "url": "https://sample.api.com/newrecord/", "method": "POST", "headers": { "Authorization": "Bearer 4f5b6f29654s36654xsvdc895b469dc0" }, "body": { "register": 1, "user": "{{'umid: ' + data.payload.source}}", "time": "{{data.receivedAt}}" }, "outputs": { "httpCode": "{{step.responseCode}}" } }, "selectNextStep": { "register_flow_2": "{{step.responseCode == '200'}}", "register_flow_fail": null } }, { "id": "register_flow_2", "stepType": "ChatAppsMessage", "inputs": { "subAccountId": "acme_corp_chatapps", "user": { "msisdn": "{{data.payload.user.msisdn}}" }, "type": "text", "content": { "text": "Hello, you are now registered, thanks !" } } }, { "id": "register_flow_fail", "stepType": "ChatAppsMessage", "inputs": { "subAccountId": "acme_corp_chatapps", "user": { "msisdn": "{{data.payload.user.msisdn}}" }, "type": "text", "content": { "text": "Hello, something went wrong, please try again later" } } } ] } } ``` Menu using WaitForReply: ```json { "trigger": "inbound_chat_apps", "subAccountId": "acme_corp", "status": "enabled", "definition": { "name": "ChatBot-123", "steps": [ { "stepType": "ChatAppsMessage", "id": "Hello", "inputs": { "subAccountId": "acme_corp", "user": { "msisdn": "{{data.payload.user.channelUserId}}" }, "type": "text", "content": { "text": "Hello, 👋\r\nThanks for contacting our team 🤖\r\n Please choose one of the option below: 🤓 \r\n 1️⃣ Technical Support \r\n 2️⃣ Product Questions \r\n 3️⃣ Sales Support \r\n 4️⃣ Billing Qestions \r\n 5️⃣ Other" } }, "outputs": { "user_msisdn": "{{data.payload.user.channelUserId}}" }, "nextStepId": "wait1" }, { "stepType": "WaitForReply", "id": "wait1", "inputs": { "timeout": "00:05:00", "channel": "whatsapp", "from": "{{data.user_msisdn}}" }, "outputs": { "reply1": "{{step.reply}}" }, "selectNextStep": { "success": "{{data.reply1 != null}}", "failure": "{{data.reply1 == null}}" } }, { "stepType": "Branch", "id": "success", "selectNextStep": { "branch1": "{{ data.reply1.payload.content.text == '1'}}", "branch2": "{{ data.reply1.payload.content.text == '2'}}", "branch3": "{{ data.reply1.payload.content.text == '3'}}", "branch4": "{{ data.reply1.payload.content.text == '4'}}", "branch5": "{{ data.reply1.payload.content.text == '5'}}" } }, { "stepType": "ChatAppsMessage", "id": "branch1", "inputs": { "subAccountId": "acme_corp", "user": { "msisdn": "{{data.user_msisdn}}" }, "type": "text", "content": { "text": "Thanks for choosing 1️⃣ Technical Support! \r\n This department will get back to you shortly" } } }, { "stepType": "ChatAppsMessage", "id": "branch2", "inputs": { "subAccountId": "acme_corp", "user": { "msisdn": "{{data.user_msisdn}}" }, "type": "text", "content": { "text": "Thanks for choosing 2️⃣ Product Questions! \r\n This department will get back to you shortly" } } }, { "stepType": "ChatAppsMessage", "id": "branch3", "inputs": { "subAccountId": "acme_corp", "user": { "msisdn": "{{data.user_msisdn}}" }, "type": "text", "content": { "text": "Thanks for choosing 3️⃣ Sales Support! \r\n This department will get back to you shortly" } } }, { "stepType": "ChatAppsMessage", "id": "branch4", "inputs": { "subAccountId": "acme_corp", "user": { "msisdn": "{{data.user_msisdn}}" }, "type": "text", "content": { "text": "Thanks for choosing 4️⃣ Billing Support! \r\n This department will get back to you shortly" } } }, { "stepType": "ChatAppsMessage", "id": "branch5", "inputs": { "subAccountId": "acme_corp", "user": { "msisdn": "{{data.user_msisdn}}" }, "type": "text", "content": { "text": "Thanks for choosing 5️⃣ other! \r\n This department will get back to you shortly" } } }, { "stepType": "ChatAppsMessage", "id": "failure", "inputs": { "subAccountId": "acme_corp", "user": { "msisdn": "{{data.user_msisdn}}" }, "type": "text", "content": { "text": "Ok if you don't reply I will chat with someone else 😥 Feel free to contact me again 👨‍💻" } } } ] } } ``` --- ## For Partner Admins: Managing Your Team As a Partner Admin, you have full control over your team's access to customer accounts and specific features. This is managed through **Roles** and **Users**. ### Setting Up and Managing Roles Roles allow you to create templates of permissions that can be assigned to your team members. This is the best way to ensure consistent access levels across your organization. 1. **Navigate to Roles:** Go to **Partners > Roles** in the left-hand menu. 2. **Create a New Role:** - Click the **+ Create a role** button. - Give the role a descriptive name (e.g., 'Support Analyst', 'Sales Viewer'). - Click on the **Modules** section to open a detailed checklist of permissions. - Select the specific modules and sub-permissions this role should have. You can grant access to entire product areas or select granular permissions like 'View Logs' or 'View Reports'. - Click **Apply** and save the new role. ![Manage users](../images/b5a6eb6e8d0a5dc2073ec28e272058bf4fedbab15a60434607486724eadb7811-Manage_users_1.png) ![Manage roles - Create a role Empty state](../images/1583a6fb76f25243b351bad2b30de992674f275df0df5a3d46fb06fb2defd84f-Manage_roles_-__Create_a_role_Empty_state.png) ![Popover window](../images/981294e834f351ddf2954b6fa7f5b89892aeff2124ac8e61fc99899e3819b1a3-Popover_window.png) 1. **Editing a Role:** To modify an existing role, find it in the list and click the **edit icon**. ### Managing Users Once your roles are defined, you can manage your users' access. 1. **Navigate to Users:** Go to **Partners > Users** in the left-hand menu. 2. **Assign Access:** Select a user from the list to manage their permissions. From their profile, you can: - **Assign a Role:** Apply a pre-defined role to grant them a standard set of permissions. - **Assign Customer Accounts:** Select the specific customer accounts that this user should be able to view. ![Manage users](../images/ad89ff5348eb030f7ea2e19de8172b82ecaccac6b7a3eb8843e6fbd884519d9f-Manage_users.png) --- ## Freshdesk [Freshdesk](https://freshdesk.com/eu/) is a customer support software providing automated helpdesk support for companies to effectively manage their customer care and support. By integrating 8x8 Chat Apps product into FreshDesk Support you get the best of both products, a simple and unique Chat Apps API with no deployment needed, as well as the best customer service front end, with advanced configurations available. ## Video Guide ## Use cases * Receive and send Chat Apps message for FreshDesk support. * Automate assigning of Chat Apps messages triggered based on status changes. ## Product scope * 8x8 Chat Apps Account * FreshDesk (Growth plan or higher) ## What you'll need * A 8x8 Connect account with Chat Apps enabled * Your FreshDesk Support account: This integration is based on Freshdesk automation features, which are only available for the **Growth** plan (or higher) * ChannelId and Access Key (Request from 8x8 Support Team for the Freshdesk Integration) ## Creating your Chat Apps integration with Freshdesk Support's automation feature #### 1. Let’s setup the first Automation, go to Settings, then Automations (under Productivity) ![freshdesk-1](../images/9371e52-Freshdesk_1.png "Freshdesk 1.png") --- #### 2. Under Ticket Updates, let’s create a new rule called "ChatApps new public comment" ![freshdesk-2](../images/a44c114-Freshdesk_2.png "Freshdesk 2.png") --- #### 3. Let’s configure the following fields ![freshdesk-3](../images/fac82d0-Freshdesk_3.png "Freshdesk 3.png") ![freshdesk-4](../images/e5dc813-Freshdesk_Config_1.png "Freshdesk 4.png") * Your URL should be: `https://chatapps.8x8.com/webhook/freshdesk/{ChanelId}?accessKey={accessKey}` * {ChanelId} and {accessKey} will be sent to you. * For example: `https://chatapps.8x8.com/webhook/freshdesk/1234-abcd-1234-abcd?accessKey=123456789` * Your JSON content should be: ```json { "event": "AgentComment", "ticketId": "{{ticket.id}}" } ``` --- #### 4. Under Ticket Updates again, let’s create another rule called **ChatApps status changed** ![freshdesk-5](../images/f5616ff-Freshdesk_5.png "Freshdesk 5.png") --- #### **5. Let’s configure the following fields:** ![freshdesk](../images/bb87138-Freshdesk_6.png "Freshdesk 6.png") ![image](../images/b7dd8a1-Screenshot_2021-04-30_at_4.09.27_PM.png "Screenshot 2021-04-30 at 4.09.27 PM.png") **URL is the same as the above** #### Your JSON now should be ```json { "event": "StatusChanged", "ticketId": "{{ticket.id}}", "ticketStatus": "{{ticket.status}}" } ``` --- #### 6. Once this is done, you will need to send to 8x8 the following information * **URL**, the path to customer Freshdesk API:https://{YOUR_SUBDOMAIN}.freshdesk.com/api/v2 * **ApiToken**, your Freshdesk ApiToken, of an admin user. * **TicketTag**, which will be added to tickets created by 8x8 (Chat Apps, for instance) * **DefaultTicketSubject**, ticket subject which will be used for a new ticket starting from an incoming message > 📘 **Optional Step** > > If you would like to send an automatic message to your users, when closing a ticket, you can use the instructions below to set it up. > > **Automatic Closing Message** * Create a new Automation rule under "Ticket Updates" Category * Set the correct tag and rules (tag should be consistent to what you used in other rules) * Select Trigger webhook - POST on `https://chatapps.8x8.com/api/v1/subaccounts/{{subaccountid}}/messages` * please replace {{subaccountid}} with your `subaccountId`which can be found in [8x8 Connect](https://connect.8x8.com/messaging/api-keys) * custom header: `{"Authorization": "Bearer YprcJ**\*\***\*\*\*\***\*\***","Content-Type": "application/json"}` *8x8 Bearer token can be found in the API Keys section of your [8x8 connect](https://connect.8x8.com) account* **Your JSON advance body:** ```json { "user": { "msisdn": {{ticket.requester.mobile}} }, "type": "Text", "content": { "text": "Thanks for your reaching out, your ticket {{ticket.id}} is now closed" } } ``` You can modify the "text" field above to include any message and use any [FreshDesk ticket field](https://support.freshdesk.com/en/support/solutions/articles/52630-understanding-dynamic-content-and-placeholders). ![FreshDesk-Closing message](../images/5dd454a-FreshDesk-Closing_message.jpg "FreshDesk-Closing message.jpg") --- ## Branches > 🚧 **[BETA]** > > This product is currently in early access. Please reach out to your account manager to get more information. > A branch is a specific type of step, allowing you to split your workflow definition, based on a condition. Here is an example of branch: ```json { "id": "step_1", "stepType": "Branch", "selectNextStep": { "branch1": "{{isCountryCode(data.payload.user.msisdn, 'SG')}}", "branch2": "{{isCountryCode(data.payload.user.msisdn, 'US')}}", "branch3": null } } ``` As you can see, the *stepType* is *Branch*. In this case, you need to define the next steps and the condition. This can be done using *selectNextStep* and by listing the branches. You can use any branch name (here we used the names *branch1*, *branch2* and *branch3*). The branch names defined here will be used as the step id for the following steps. In the example above the next step will start with "id": "branch1", Inside each branch, you need to define a condition, for more detail, see the **Scripting** section below. In the example above, we are creating the following logic: - branch1 will be selected if the source number has a Singapore country code - branch2 will be selected if the source number has a US country code - branch3 will be selected otherwise | Property | Description | Type | |------------|----------------------------------------------------------------------------------------------------------------------------------|--------| | id | Unique id of the step. | string | | stepType | Step type. | string | | inputs | Wait step supports the following input parameters. - **minutes**: Number of minutes to wait before executing the next step. | object | | selectNextStep | Step ids of the branches and the conditions. | string | --- ## Getting started with Automation API > 🚧 **[BETA]** > > This product is currently in early access. Please reach out to your account manager to get more information. > > ## Overview The Automation API allows you to create and manage complex business workflows. A workflow is a sequence of triggers, conditions and actions which we will be running for you, it can be seen as a business logic. We will be using the following terms in this document: - **Workflow definition**, the blueprint of a business flow. - **Workflow instance**, the runtime object that executes the business logic defined in the corresponding workflow definition. - **Trigger**, an external event that starts a workflow without manual intervention. - **Step**, a basic building block of a workflow definition which represents an action to be taken. - **Branch**, a step that allows you to define alternate paths for the workflow based on conditions. - **Workflow context**, data captured in a workflow instance which persists between steps. ## Use Cases The Automation API allows you to tackle many business processes, here are some examples of the most common use cases: - **Auto Reply**: for each incoming message (SMS or ChatApps) I want to send an automatic reply to the user. It can be to acknowledge reception or to share a link for support enquiries. - **Out of Office**: outside of business hours, I want to send an automatic reply to the users, to let them know when I will get back to them. - **Fallback**: for each undelivered outbound message, I want to call another API to make sure my message got delivered through another channel. - **Split messages**: for all messages coming from a +1 (US) number, I want to send them to a CRM using a custom API call. For all other messages, I want to send them to a different CRM. - **Custom integration**: I want some of the incoming messages to be pushed to my ordering system, for this I will use the Automation service to make a custom API call to my system. - **Content based rule**: I'm running a campaign where users need to send a specific message to my number. I can filter only the message with this content, to confirm customers they are enrolled and push the registered user numbers to my system, using an API call. - **Mix and match**: mix any of the examples above. For example, for messages outside of business hours coming from a +44 (UK) number, I want to send an auto reply. For all messages coming from +65 (Singapore) number, I want to do a custom API call and for all the other messages do nothing. These are just some example, as you can design the workflow definitions, you can create many more flows to support other use cases. Feel free to contact us for support. ## Authentication Automation API uses Bearer authentication scheme. All requests to automation server must contain the *HTTP authorization* header with the value *Bearer {apiKey}* where *{apiKey}* for the account can be obtained from 8x8 Connect [https://connect.8x8.com/](https://connect.8x8.com/). The account must be registered in the automation service before it can be used (please reach out to your account manager for more information). ## How to use the API Here is how you would likely interact with the Automation API: **WORKFLOW CREATION** 1) Create a new workflow definition, you are submitting your blueprint -> [/connect/reference/create-definition](/connect/reference/create-definition) **WORKFLOW DEFINITION MANAGEMENT** 2) Get your workflow definitions, to make sure your definition is there -> [/connect/reference/get-all-definitions](/connect/reference/get-all-definitions) 3) Retrieve your workflow, based on the workflowId, to verify it -> [/connect/reference/get-specific-definition](/connect/reference/get-specific-definition) 4) If you want to modify your definition, you can use this -> [/connect/reference/update-existing-definition](/connect/reference/update-existing-definition) 5) If you want to delete your definition, you can use this -> [/connect/reference/delete-definitions](/connect/reference/delete-definitions) **WORKFLOW INSTANCE MANAGEMENT** 6) To test your workflow, you can trigger it manually -> [/connect/reference/start-workflow-instance](/connect/reference/start-workflow-instance) 7) To retrieve the workflow instance of a specific workflow definition -> [/connect/reference/get-workflow-instances](/connect/reference/get-workflow-instances) 8) To suspend, resume or terminate a workflow instance -> [/connect/reference/patch-workflow-instance](/connect/reference/patch-workflow-instance) 9) To get errors of a workflow instance -> [/connect/reference/get-instance-errors](/connect/reference/get-instance-errors) As you can see, only the step #1 is mandatory. If you know that your workflow is valid, and that you can test it without the API (by actually sending an incoming message for example), you only need to perform the first step (create a workflow definition). The other items are here to help you manage your workflow definition, test them manually and debug them. ## Workflow definition example Here is a simple example of a workflow definition. This definition contains the following attributes: - the trigger is any Inbound Chat Apps message on the subaccount acme_corp_chatapps - there are no conditions, all instances will result in sending a Chat Apps message - the instance will send an auto reply message to any incoming Chat Apps message ```json { "subAccountId": "acme_corp_chatapps", "trigger": "inbound_chat_apps", "status": "enabled", "definition": { "name": "Auto Reply ChatApps", "steps": [ { "id": "send_CA", "stepType": "ChatAppsMessage", "inputs": { "subAccountId": "acme_corp_chatapps", "user": { "msisdn": "{{data.payload.user.msisdn}}" }, "type": "text", "content": { "text": "Hello, thank you for your message, we will get back to you as soon as possible." } } } ] } } ``` --- ## Getting started with Contacts API Our Contacts API will help you manage your contacts through our RESTful api endpoints. It will allow you to: - Create a single contact or batch of contacts - Update information about a particular contact - Delete a single contact or batch of contacts - View a contact's information - Create a contact group and add contacts to that group - Blacklist a group of contacts and more just to name a few. ## Server Regions To ensure the use of the correct platform deployment region, it is necessary to modify the base URL to correspond with the provisioned region of your account. Refer to the table below for the appropriate base URL associated with each platform region. For more information on platform regions, please visit the following [page](/connect/docs/platform-deployment-regions#api-endpoints-and-platform-region). **List of server URLs:** | API Region | Base URL | | :------------- | :-------------------------------- | | Asia (default) | | | Europe | | | North America | | | Indonesia | | --- ## Getting started with Number Lookup API This API allows customers to perform various levels of lookup on a given phone number to retrieve information ranging from: - basic phone number type, country code - to more advanced MNP/HLR based data such as current/ported mobile operator, live presence and roaming status. ## Server Regions To ensure the use of the correct platform deployment region, it is necessary to modify the base URL to correspond with the provisioned region of your account. Refer to the table below for the appropriate base URL associated with each platform region. For more information on platform regions, please visit the following [page](/connect/docs/platform-deployment-regions#api-endpoints-and-platform-region). **List of server URLs:** | API Region | Base URL | | :------------- | :------------------------------ | | Asia (default) | | | Europe | | | North America | | | Indonesia | | --- ## Getting started with SMS API The SMS API enables 8x8 customers to both send and receive SMS messages through a variety of use cases: - **Send SMS Messages:** Send individual SMS messages on demand. - **Bulk Messaging:** Send up to 10,000 SMS messages in a single API request for large-scale campaigns. - **Delivery Status Updates:** Receive callbacks with real-time delivery status for each sent SMS. - **Inbound SMS Handling:** Process inbound SMS messages sent to virtual phone numbers. - **SMS Engage Surveys:** Invite users to participate in SMS engage surveys, either individually or in bulk, and capture their responses. - **OTP Feedback Transmission:** Provide feedback on OTP SMS outcomes to dynamically optimize SMS delivery quality. - **Activity Retrieval:** Retrieve detailed SMS activity logs for analysis and data reconciliation. > 📘 Downloading SMS APIs (OAS File) > > You can download the OAS File - **[Click Here](https://github.com/8x8Cloud/public-developer-docs/blob/master/docs_oas/connect/sms_api.json)** > > **_ Please do take note that the file provides all the SMS APIs so please look through the .OAS file and select the specific SMS API(s) required._** > ## Server Regions To ensure the use of the correct platform deployment region, it is necessary to modify the base URL to correspond with the provisioned region of your account. Refer to the table below for the appropriate base URL associated with each platform region. For more information on platform regions, please visit the following [page](/connect/docs/platform-deployment-regions#api-endpoints-and-platform-region). **List of server URLs:** | API Region | Base URL | | :--------------------- | :----------------------- | | Asia Pacific (default) | | | North America | | | Europe | | | Indonesia | | --- ## Getting Started(Docs) In object-oriented programming, we use objects with attributes to organize our programs and build our applications. 8x8 SMS API uses the same organization and the SMS you send are also objects with their set of attributes. **Let’s have a closer look to understand better how it works:** * The object below represents a message as expected by most common 8x8 SMS API endpoint `https://sms.8x8.com/api/v1/{subAccountId}/single`. We can see that it is composed of the following attributes: ```json { "source": "Developer", "destination": "+6512345678", "clientMessageId": "MyBd00001", "text": "Hello, World!", "encoding": "AUTO", } ``` * We can see that it is composed of the following attributes: * **source** * **destination** * **clientMessageId** * **text** * **encoding** * Each of those attributes is linked to the standard parameters used in the telecommunications industry and are understood and expected by mobile carriers around the World. **Let’s go over the different attributes of an SMS one by one!** ### 1. Source: SMS senderID * **What is a senderID?** Also called TPOA (Transmission Path Origin Address), this parameter carries the SMS sender’s number or designation. Depending on the local constraints enforced by the mobile operators, different types of senderIDs may or may not be available on mobile networks across the World. #### **What types of senderIDs are available?** #### A) Numeric SenderIDs * They can be made of up to 16 digits usually representing a phone number in the cases when the SMS invites the user to respond or to call back. * According to the length and format of the numeric senderIDs, they can be segmented in 3 sub-types. * **The 3 different types of numeric senderIDs:** ##### Short-code * Example: *123* * 3 to 7 digits in length according to the countries * They are digit sequences shorter than telephone numbers that have been designated to be memorable. * They are usually used for SMS notifications or value added service based on 2-Way SMS interactions (polls, challenges, information requests, unsubscriptions, etc.).##### Local long-code * Ex: *91046180* (Local to Singapore) * Their format varies according to the countries, but are usually between 7 and 12 digits in length. * They are digit sequences that represent telephone numbers without the international prefix. * They are usually used in the cases when the senderID must be identified as an active phone number that can be called and messaged (ex: sending SMS notifications with the support phone number of your company).##### International long-code * Ex: *+6591056180* (Singaporean international long code) * They can be made of up to 15 digits and should start by a “+” sign. * They are telephone number in the international format (starting with the international prefix and followed by the local long code stripped from its leading 0). * They have the same purpose as the local long-code but can be used in an international context and the replies will be routed back correctly to the telephone number even from a different country. #### B) Alphanumeric SenderIDs * Ex: *cpaas* * They can be made of a combination of up to 11 digits and uppercase of lowercase letters and spaces. * They are used for identifying your service name or brand name to your users and consumers receiving the message: the recipient will see the message as coming from your brand name as if he had registered a name for a number in his phone contacts. * It is not possible to reply to an alphanumeric senderID, the response will not be routed back to any telephone number. Considering this, alphanumeric senderIDs should be only used with opt-in users who have been given a means to opt-out of your SMS notifications. It is recommended to include a means to opt-out at the end of your message. *To check the availability of a specific kind of senderID in a country or for a mobile network, please contact [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) to find out more.* ### 2. Destination: the recipient's phone number * The destination of an SMS is the phone number it’s being sent to. * 8x8 CpaaS enables sending SMS to mobile phone numbers in all the countries across the world. * **What should be the destination format?** * A destination phone number is made of an international prefix (ex: +65 for Singapore) and a local phone number (ex: 91046180) concatenated together (ex: +6591046180). * *Nb: 8x8 API accept both international and national formats (for national you have to specify the country in the dedicated country field in the API).* ### 3. clientMessageId: the SMS identification code * MessageIDs formats and rules will differ from one messaging provider to another but they will almost always be used to associate a specific single message with a unique reference that you can store retrieve information about this message later. * **What kind of messageids are being used on 8x8 CPaaS?** #### A) 8x8 CPaaS messageids *(automatically generated)* * By default, for each message, 8x8 will generate a messageid also referred as UMID (unique message ID). These “internal” messageids allow 8x8 to manage the millions of messages that are processed every hour while helping you troubleshoot your integration and routing issues. 8x8 messageids / UMID are the universal message references when using 8x8. #### B) clientMessageId *(personalized ids)* * You have the ability to use your own messageids and that is the purpose of the clientMessageId parameter that you can find in the message object. ### 4. Text and encoding: the message contents #### A) The message text (what you send) * The message text is the content of your SMS that is being sent to your recipient. **How long can the message text be?** * If your text is being sent with the encoding GSM7bit and is smaller or equal to 160 characters then it accounts for one message part and you will be billed for one message. * If your text is being sent with the encoding UNICODE and is smaller or equal to 70 characters then it accounts for one message part and you will be billed for one message. * According to the local constraints that apply to the country where you are sending your messages to, it might be possible to send messages longer than the limits mentioned above by concatenating several parts of message in one message. If this is available you have nothing to do: 8x8 CPaaS platform will automatically splits your long message into smaller chunks that will be sent to the recipient handset with a special parameter that allows to reassemble them. This is called SMS concatenation. * You can send up to 10 parts of messages in one SMS. **Consideration about multiparty SMS and lengths:** * According to the encoding of the SMS, the number of SMS accounted per message will be proportional to the length of the text: * For GMS7 messages: * If the total length of the message is inferior or equal to 160 characters then the first and only message part can accomodate 160 characters. * If the total length is superior to 160 characters then each message part can contain 153 characters (less characters can be fit into one part as extra data space is taken to concatenate the SMS on the destination handset) * For Unicode messages: * If the total length of the message is inferior or equal to 70 characters then the first and only message part can accomodate 70 characters. * If the total length is superior to 70 characters then each message part can contain 67 characters (less characters can be fit into one part as extra data space is taken to concatenate the SMS on the destination handset) * If you send a message with a length equal to x parts of message, you will be billed for x SMS. #### B) The message encoding (which character set to use) **Which message encoding formats are supported?** 8x8 API supports two different encoding formats: GSM7bit and UNICODE (UCS2). **How do you select the message encoding to apply?** * When submitting requests to the API to send messages you have a choice between letting 8x8 platform automatically detect the encoding format that should be used for your message or forcing the platform to use one of the two encoding formats above (7-bit GSM or UNICODE). * Although 8x8 automatic encoding format detection is convenient and will do a great job at ensuring that your contents are delivered to your recipients in an efficient and readable manner, it is a good thing to understand why SMS platforms such as 8x8 allows you to use different encoding formats. #### About the 7-bit GSM encoding format * 7-bit GSM is a lightweight but limited encoding format. * A character is stored in 7 bits which allows for a maximum of 128 distinct combinations. * The advantage is that 7-bit GSM allows more characters per message part (up to 160 as detailed above). * The drawback is that the character set is quite limited and will works quite well for plain English content but for example cannot be used for Chinese or Arabic characters. #### About the Unicode encoding format * The Unicode encoding format has a much larger character-set (more than 128,000) spread across various languages scripts and symbols sets. * A character is stored in 16 bits. * You would want to use the Unicode encoding format when sending messages containing Chinese, Thai or Arabic characters for example. * The drawback of the Unicode encoding format is that it is much heavier than the 7-bit GSM. Since each character takes 9 more bits, the message parts containing Unicode encoded data are limited to 70 characters versus 160 for 7-bits GSM --- ## Constructing WhatsApp Template Send Requests Learn how to properly format and send WhatsApp template messages through the 8x8 API using both manual and automated approaches. This guide covers everything from basic template structure to advanced scripting for bulk template preparation. --- ## Overview WhatsApp Business templates are pre-approved message formats that allow businesses to initiate conversations with customers. This guide demonstrates two methods for sending these templates via the 8x8 API: | Method | Description | Best For | | --- | --- | --- | | Manual | Step-by-step process using curl and JSON payloads | One-off messages, learning the API | | Automated | Automated generation of ready-to-use cURL commands | Multiple templates, production workflows | --- ## Prerequisites * 8x8 API credentials (Account ID, Channel ID, Subaccount ID, API Key) * WhatsApp recipient phone number (E.164 format, e.g., +14155551212) * WhatsApp templates created and approved either via the [Connect portal](/connect/docs/whatsapp-templates-management) or [API](/connect/reference/add-whatsapp-template) * [curl](https://curl.se/) installed * For automated method: [Node.js](https://nodejs.org/) (v16+), `npm install dotenv axios` --- ## Manual Method ### 1. Fetch Templates Run the following command in a terminal ```bash curl -s -X GET "https://chatapps.8x8.com/api/v1/accounts/{your_account_id}/channels/{your_channel_id}/templates" \ -H "Authorization: Bearer {{your_api_key}}" \ -H "Accept: application/json" > templates.json ``` ### 2. Extract Template Details Open `templates.json` in a text editor. Note: * `templateName` as per the GET Templates response (or `name`) in the corresponding Send message we're trying to compose * `language` * `components` (for required parameters) --- ### 3. Base API Request Structure Every WhatsApp template message request to the 8x8 API must include the following fields: ```json { "user": { "msisdn": "{{recipientPhoneNumber}}" }, "type": "template", "content": { "template": { "name": "your_template_name", "language": "template_language_code", "components": [ /* see examples below */ ] } } } ``` * `user.msisdn`: The recipient's phone number in E.164 format. * `type`: Always `"template"` for template messages. * `content.template.name`: The template name as shown in your templates list. * `content.template.language`: The language code (e.g., `"en"`). * `content.template.components`: An array of components (see examples below). --- ### 4. Compose the Message Payload Below are examples of the full payload for different template types. Replace the `components` array as needed. #### Simple Template (No Parameters) ```json { "user": { "msisdn": "{{recipientPhoneNumber}}" }, "type": "template", "content": { "template": { "name": "{{your_template_name}}", "language": "{{language_code}}", "components": [] } } } ``` #### Media Template Example ```json { "user": { "msisdn": "{{recipientPhoneNumber}}" }, "type": "template", "content": { "template": { "name": "{{your_template_name}}", "language": "en", "components": [ { "type": "header", "parameters": [ { "type": "image", "url": "{{header_image_url}}" } ] } ] } } } ``` #### Body Parameters Example ```json { "user": { "msisdn": "{{recipientPhoneNumber}}" }, "type": "template", "content": { "template": { "name": "{{your_template_name}}", "language": "en", "components": [ { "type": "body", "parameters": [ { "type": "text", "text": "{{body_text_1}}" } ] } ] } } } ``` #### AUTHENTICATION Template Example ```json { "user": { "msisdn": "{{recipientPhoneNumber}}" }, "type": "template", "content": { "template": { "name": "{{your_template_name}}", "language": "en", "components": [ { "type": "body", "parameters": [ { "type": "text", "text": "{{otpCode}}" } ] }, { "type": "Button", "subType": "url", "index": 0, "parameters": [ { "type": "text", "text": "{{otpCode}}" } ] } ] } } } ``` --- ### 5. Send the Message Save your payload to `message.json` and run the command below in the terminal ```bash SUBACCOUNT_ID="your_subaccount_id" API_TOKEN="your_api_token" curl -X POST "https://chatapps.8x8.com/api/v1/subaccounts/$SUBACCOUNT_ID/messages" \ -H "Authorization: Bearer $API_TOKEN" \ -H "Content-Type: application/json" \ -d @message.json ``` --- ## Automated Method ### 1. Setup * Place [generate-curl-scripts.js](https://gist.github.com/harrism04/c2ed6e3b4a7c7f888e3bdfc75f0cb91f) in your project directory. ```typescript // @ts-check require('dotenv').config(); const axios = require('axios'); const fs = require('fs'); const path = require('path'); const API_BASE_URL = process.env.API_BASE_URL || 'https://chatapps.8x8.com'; const ACCOUNT_ID = process.env.ACCOUNT_ID || ''; const CHANNEL_ID = process.env.CHANNEL_ID || ''; const SUBACCOUNT_ID = process.env.SUBACCOUNT_ID || ''; const API_KEY = process.env.API_KEY || ''; const OUTPUT_DIR = 'generated_curl_scripts'; function getComponents(template) { if ( (template.type && template.type.toUpperCase() === 'AUTHENTICATION') || (template.category && template.category.toUpperCase() === 'AUTHENTICATION') ) { return [ { "type": "body", "parameters": [ { "type": "text", "text": "{{otpCode}}" // Using placeholder for OTP } ] }, { "type": "Button", "subType": "url", "index": 0, "parameters": [ { "type": "text", "text": "{{otpCode}}" // Using placeholder for OTP } ] } ]; } // For other templates, generate descriptive placeholders based on structure const components = []; if (template.components) { template.components.forEach(component => { const componentType = component.type.toUpperCase(); if (componentType === 'HEADER') { const format = component.format?.toUpperCase(); if (format === 'TEXT' && component.text) { const placeholders = findPlaceholders(component.text); if (placeholders.length > 0) { components.push({ type: "header", parameters: placeholders.map(num => ({ type: "text", text: `{{header_text_${num}}}` })) }); } } else if (['IMAGE', 'VIDEO', 'DOCUMENT'].includes(format)) { components.push({ type: "header", parameters: [{ type: format.toLowerCase(), url: `{{header_${format.toLowerCase()}_url}}` }] }); } else if (format === 'LOCATION') { components.push({ type: "header", location: { latitude: "{{header_loc_lat}}", longitude: "{{header_loc_lon}}", name: "{{header_loc_name}}", address: "{{header_loc_addr}}" } }); } } else if (componentType === 'BODY' && component.text) { const placeholders = findPlaceholders(component.text); if (placeholders.length > 0) { components.push({ type: "body", parameters: placeholders.map(num => ({ type: "text", text: `{{body_text_${num}}}` })) }); } } else if (componentType === 'BUTTONS' && component.buttons) { component.buttons.forEach((button, buttonIndex) => { if (button.type.toUpperCase() === 'URL' && button.url) { const placeholders = findPlaceholders(button.url); if (placeholders.length > 0) { components.push({ type: "button", subType: "url", index: buttonIndex, parameters: placeholders.map(num => ({ type: "text", text: `{{button_${buttonIndex}_url_param_${num}}}` })) }); } } }); } }); } return components; } // Extracts {{n}} placeholders from a string (copied from static/script.js) function findPlaceholders(text) { if (!text) return []; const regex = /{{(\d+)}}/g; const placeholders = new Set(); let match; while ((match = regex.exec(text)) !== null) { placeholders.add(parseInt(match[1], 10)); } return Array.from(placeholders).sort((a, b) => a - b); } async function fetchTemplates() { const url = `${API_BASE_URL}/api/v1/accounts/${ACCOUNT_ID}/channels/${CHANNEL_ID}/templates`; const headers = { Authorization: `Bearer ${API_KEY}` }; const { data } = await axios.get(url, { headers }); return data.templates || []; } function generateCurlCommand(template) { const payload = { user: { msisdn: '{{recipientPhoneNumber}}' }, // Use placeholder here type: 'template', content: { template: { name: template.templateName || template.name, language: template.language || 'en', components: getComponents(template) } } }; const jsonString = JSON.stringify(payload, null, 2); const escapedJsonString = jsonString.replace(/'/g, "'\\''"); const curlCommand = `curl -X POST \\ '${API_BASE_URL}/api/v1/subaccounts/${SUBACCOUNT_ID}/messages' \\ -H 'Authorization: Bearer {{apiKey}}' \\ -H 'Content-Type: application/json' \\ -d '${escapedJsonString}'`; return curlCommand; } (async () => { try { if (!fs.existsSync(OUTPUT_DIR)) { fs.mkdirSync(OUTPUT_DIR); } const templates = await fetchTemplates(); if (templates.length === 0) { console.log('No templates found.'); return; } console.log(`Generating cURL scripts for ${templates.length} templates in ./${OUTPUT_DIR}/`); for (const template of templates) { const templateName = template.templateName || template.name; const language = template.language || 'en'; const filename = `${templateName}_${language}.sh`; const filepath = path.join(OUTPUT_DIR, filename); const curlCommand = generateCurlCommand(template); const scriptContent = `#!/bin/bash # cURL command for template: ${templateName} (${language}) # Category: ${template.category || 'N/A'} # Replace {{apiKey}} with your actual 8x8 API Key # Replace placeholder values in the -d payload as needed, including {{recipientPhoneNumber}} ${curlCommand} `; fs.writeFileSync(filepath, scriptContent); fs.chmodSync(filepath, '755'); // Make the script executable } console.log('cURL scripts generated successfully.'); } catch (err) { console.error('Failed to generate cURL scripts:', err.message); } })(); ``` * Install dependencies: ```bash npm install dotenv axios ``` * Create a `.env` file: ```bash API_BASE_URL=https://chatapps.8x8.com # replace with endpoint associated with your DC region https://developer.8x8.com/connect/docs/platform-deployment-regions#api-endpoints-and-platform-region ACCOUNT_ID=your_account_id CHANNEL_ID=your_channel_id SUBACCOUNT_ID=your_subaccount_id API_KEY=your_api_key ``` ### 2. Generate cURL Scripts ```bash node generate-curl-scripts ``` * This creates a `generated_curl_scripts` directory with `.sh` files for each template. ### 3. Use the Generated Scripts * Edit the `.sh` file: * Replace `{{apiKey}}` with your API Key. * Replace placeholders (e.g., `{{recipientPhoneNumber}}`, `{{otpCode}}`) with real values. * Run the script: ```bash ./your_template_en.sh ``` --- ## Tips & Best Practices * Always replace placeholders with real values before sending. * Test with a non-production recipient first. * Review API responses or [delivery receipts](/connect/reference/delivery-receipts-for-outbound-chatapps) for errors. You can also check the [Logs](/connect/docs/messaging-apps#logs) and take action from there. * From time to time, Meta might not deliver messages to maintain a healthy ecosystem (Error Code: 131049 and similar), so you can try sending it to a secondary non-production recipient or try again later --- ## RCS RCS Business Messaging (RBM) is designed to enable rich, interactive communication between businesses and consumers—all within the default messaging app on Android devices. It’s powered by Rich Communication Services (RCS), an industry protocol standardized by the GSMA (Global System for Mobile Communications Association) and adopted by mobile carriers and device manufacturers worldwide to modernise texting. **Key Features:** • Branded messages with your business name, logo, and verified status. • Rich media support: Send high-quality images, videos, carousels, and file attachments. • Interactive messaging: Use buttons for calls, maps, website links, quick replies, and more. • Delivery and read receipts: Know when messages are delivered and seen. • SMS fallback: Automatically sends as SMS when RCS is unavailable on the device. **Why It’s Valuable for Businesses:** • Drives higher engagement than traditional SMS with app-like experiences. • Builds trust and authenticity through verified branding. • Supports two-way conversations (ideal for updates, promotions, reminders, and support). ![image](../images/25348533873ca8cdf3c734bde3024e321448d9e5e8987ae4927f62b1c96768df-Rich-card.png) --- **Key Terms:** **RCS Sender Agent** - An RCS agent or RCS Sender Agent is a digital identity that represents a brand in a customer's Rich Communication Services (RCS) messaging experience. RCS agents use the RCS Business Messaging (RBM) API to communicate with users through messages, events, and requests. **RCS** - “RCS” means the Rich Communications Services message protocol that allows users to send texts, photos, videos, and more. RCS provides a richer message feature set than the legacy SMS/MMS message protocol. **RBM** - “RBM” means RCS Business Messaging, otherwise known as non-consumer RCS and provides a messaging protocol to allow businesses to engage and interact with customers using rich, interactive message features. **Rich Media Messaging** - “Rich Media Messaging” means non-consumer text messages that include images and videos. **Basic Messaging** - “Rich Messaging” means non-consumer text-only messages. --- ## WhatsApp Image Optimization When sending WhatsApp messages with media content, the speed and reliability of your image delivery directly impacts message success rates. This guide will help you optimize your images for WhatsApp campaigns and troubleshoot common performance issues. ## Image Requirements ### File Specifications * Maximum file size: 5 MB, **( below 1 MB** **recommended)** * [Supported formats](/connect/docs/supported-chat-apps-content-type#whatsapp): JPEG, **PNG (recommended)** * Recommended dimensions: 1200x630 pixels ## Performance Benchmarks ### Server Response Expectations * Ideal response time: below 2 seconds * Recommended server latency: Consistently under 2 seconds ## Troubleshooting Methodology ### Diagnostic Tools 1. **Postman Testing** Create GET request to image URL ```bash curl -v -X GET "YOUR_IMAGE_URL" ``` **Example:** ```bash curl -v -X GET "https://filesamples.com/samples/image/jpeg/sample_5184%C3%973456.jpeg" ``` ![It took 5.62 seconds to fetch the image which is too long for APIs in genera](../images/b23616a67dc585bf2e5838f96afbb115139ff853225ef2a639996fe9341a2751-image.png)It took 5.62 seconds to fetch the image which should be optimized further. After going through this guide, you should be able to repeat the test above and achieve < 1 second response time. --- ## Common Performance Bottlenecks ### Image-Related Issues * Oversized image files and/or dimensions * Non-optimized image compression * Slow hosting infrastructure * Geographical server distance or geo restriction ### Recommended Optimization Strategies 1. **Image Compression** * Use tools like *TinyPNG* or *Squoosh* to reduce file size without compromising visual quality * More often than not, your graphic design tool might have a built-in functionality to compress images while preserving quality * Target < 500 KB for optimal performance 2. **Hosting Optimization** * Implement [Content Delivery Network (CDN)](https://www.pingdom.com/blog/a-beginners-guide-to-using-cdns-2/) * Choose geographically distributed hosting if you're a multinational brand * Ensure global accessibility * Use high-performance cloud storage --- ## Debugging Checklist ### Quick Diagnostic Steps * Verify image URL accessibility * Check image file size * Test server response time * Validate image [format compatibility](/connect/docs/supported-chat-apps-content-type#whatsapp) * Confirm public accessibility without authentication --- --- ## Zero-tap and One-tap WhatsApp Authentication Template ## WhatsApp Zero-tap and One-tap Authentication Templates WhatsApp authentication templates can be configured to provide a more seamless authentication experience on Android devices through Zero-tap (automatic code insertion) or One-tap (user confirms code insertion) methods. This guide explains how to modify your existing Copy Code authentication templates to enable these features. > > If you're new to WhatsApp as an authentication method for your app, we recommend starting with the standard 'copy code' delivery method and evaluating its performance. Consider implementing Zero-tap/One-tap delivery after you've established your baseline authentication flow and identified opportunities to reduce drop-off rates. > > > > ⚠️ **Important: Zero-tap and One-tap authentication methods only work on Android devices. Non-Android devices will automatically fall back to Copy Code button.** > > ### Getting Started #### When to Use Zero-tap/One-tap Zero-tap and One-tap delivery methods are particularly beneficial when: * The majority of your users are on Android devices * Your authentication funnel shows significant drop-offs during the authentication code entry step * You want to reduce friction in critical user journeys like registration or purchases * User experience and conversion speed are key priorities #### Prerequisites * An approved WhatsApp authentication template, created via [API](/connect/reference/add-whatsapp-template) or [Connect Portal](/connect/docs/whatsapp-templates-management#creating-templates) * Access to Meta Business Suite with appropriate permissions * Android app package name and signature hash information #### Before You Begin This guide covers template configuration in Meta Business Suite. However, to enable Zero-tap/One-tap functionality, you'll need to implement additional components in your Android app after completing this guide. Details about the implementation will be covered in the **Next Steps** section. --- ### Delivery Methods * **Zero-tap**: Code is automatically inserted without user interaction. Best for scenarios where users have explicitly agreed to automatic authentication. * **One-tap**: Users approve code insertion with a single tap. Provides additional security while maintaining convenience. * **Copy Code**: Users copy the code and paste it into the browser or application. Good for scenarios where most of your users are not on Android. This is the default method. --- ### Configuring Your Template 1. Log in to [Meta Business Suite](https://business.facebook.com/latest/settings/) 2. From the left panel, select "WhatsApp accounts" and choose your WhatsApp account ![image](../images/d884e58ee928e9d74b52b644d28a9109fc3e931a95335f57164c362e50948be7-image.png) 3. Click "WhatsApp Manager" on the right side of the screen 4. In WhatsApp Manager, select "Manage templates" from the left panel ![image](../images/f0e373550e092bdeb1785dc472836f74438411e34d860687b9a041ba16e2ae13-image.png) 5. Locate and click on your approved authentication template that was created via 8x8 platform, then select "Edit template". If you don't see one, please follow the [guide](/connect/docs/whatsapp-templates-management#creating-templates) and ensure you meet all other pre-requisites mentioned at the beginning of this guide. ![image](../images/7a1fae0359ae0a3ed2a7de28018abba9139a1fa3c929cb819c1189db8fbe4c52-image.png) 6. Choose your preferred Code Delivery method: * Zero-tap autofill * One-tap autofill * Copy code (default) > > Note: For Zero-tap authentication, ensure compliance with Meta's [best practices](https://business.facebook.com/business/help/285737223876109) > > > ![image](../images/54f3382a0ac7e07d30796ad96d426b707e18914d5f45d4e8a54188502c2c2ad0-image.png) 7. Enter your Android app information: * Package name * App signature hash ![image](../images/c37eaf2b7d1375c66a1aa82499f3081840c0c6878fa9cd538b1b3e6b25ce751d-image.png) 8. **Optional**: Adjust the message validity period (time-to-live) at the bottom of the page. This ensures the WhatsApp message doesn't get sent beyond the validity period, which allows you to orchestrate alternative channels such as SMS OTP or Voice OTP ![image](../images/c2d8279519f731a7d15266581c48ccfe57e5184dc1fc24487d9754c832f379ff-image.png) 9. Click Submit to save your changes --- ### Implementation Guide The following sections contain technical details for Android app implementation and should be reviewed by your development team. #### Testing Your Template Before implementing in your own app, you can: * Use WhatsApp's official [sample OTP app](https://github.com/WhatsApp/WhatsApp-OTP-Sample-App) to validate your template configuration * Test with both Android and iOS devices #### Next Steps After validating your template configuration, you'll need to implement the required WhatsApp OTP handshake in your Android app. This security mechanism enables Zero-tap/One-tap functionality through either: 1. WhatsApp's OTP Android SDK (Recommended) * Simpler implementation * Available through Maven Central * Handles most of the complexity for you 2. Manual Implementation * Implement custom Android activities and intent filters * Handle the handshake process directly * More flexibility but requires more code > 📘 **For technical implementation details and code examples for both approaches, refer to Meta's [OTP handshake documentation](https://developers.facebook.com/docs/whatsapp/business-management-api/authentication-templates/autofill-button-authentication-templates#handshake).** > > #### Troubleshooting * Verify package name and signature hash match between template configuration and your Android app * Ensure [handshake](https://developers.facebook.com/docs/whatsapp/business-management-api/authentication-templates/autofill-button-authentication-templates#initiating-the-handshake) is initiated before sending the authentication message (within 10 minutes) * Check that the user has [WhatsApp installed](https://developers.facebook.com/docs/whatsapp/business-management-api/authentication-templates/autofill-button-authentication-templates#checking-if-whatsapp-is-installed) and is logged in * Confirm your app has the proper activity defined to receive the authentication code #### Best Practices * Clearly inform users about automatic code insertion in your message * Allow users to choose their preferred authentication method See example below: ![image](../images/df4d3135f65bdb880916d73f43b1743935066adf88eb9b75650d4198e4e65e9e-image.png) #### Using the Template You can send authentication messages using the same [Authentication template payload](/connect/reference/send-message) regardless of the delivery method chosen. The delivery method will be automatically determined based on the user's device type. --- ## WhatsApp Template Validity Period (TTL) ## Configuring Validity Period for WhatsApp Templates WhatsApp allows you to define a specific validity period or delivery window, also known **Time-To-Live (TTL)**, for **Utility** and **Authentication** message templates. This determines how long WhatsApp will attempt to deliver the message to the recipient's device. Setting an appropriate validity period is crucial for: * **Time-sensitive messages:** Ensuring One-Time Passwords (OTPs), notifications or critical alerts aren't delivered after they become irrelevant. * **Coordinating fallback strategies:** Triggering alternative channels (like SMS, email, or voice) only *after* the WhatsApp message validity period has expired, preventing duplicate messages. This guide shows you how to configure the validity period for your templates within the Meta Business Suite. --- > 👍 > > Refer to [Messaging Apps fallback management guide](/connect/reference/chatapps-fallback-management) to ensure your message delivery orchestration align with the WhatsApp template validity period, preventing duplicated messages > > ### TTL Defaults, Ranges, and Compatibility The ability to customize Time-To-Live (TTL), the specific ranges allowed, and the default behavior depend on the template type and potentially the API being used: | Feature | Authentication | Utility | Marketing | | :--------------------- | :------------------------- | :--------------------- | :------------------------------- | | **Default TTL** | 10 minutes | 30 days | 30 days | | **Compatibility** | Cloud API + On-Premise API | Cloud API only | Marketing Messages (MM) Lite API | | **Customizable Range** | 30 seconds to 15 minutes | 30 seconds to 12 hours | 12 hours to 30 days | *Note: This guide focuses primarily on configuring TTL for **Utility** and **Authentication** templates, where customization is most impactful for time-sensitive delivery and fallback orchestration.* --- ### Prerequisites * Access to [Meta Business Suite](https://business.facebook.com/latest/settings/) with appropriate permissions for your WhatsApp Business Account. * An existing, approved **Utility** or **Authentication** WhatsApp template created via the 8x8 platform ([API](/connect/reference/add-whatsapp-template) or [Connect Portal](/connect/docs/whatsapp-templates-management#creating-templates)). --- ### Configuring the Template Validity 1. Log in to [Meta Business Suite](https://business.facebook.com/latest/settings/). 2. Navigate to **WhatsApp accounts** from the left-hand menu and select the relevant WhatsApp account. ![image](../images/d884e58ee928e9d74b52b644d28a9109fc3e931a95335f57164c362e50948be7-image.png) 3. Click the **WhatsApp Manager** button, usually located on the right side of the screen. 4. In the WhatsApp Manager interface, select **Message templates** from the left-hand navigation panel. ![image](../images/f0e373550e092bdeb1785dc472836f74438411e34d860687b9a041ba16e2ae13-image.png) 5. Locate the specific **Utility** or **Authentication** template you wish to configure. Click on the template name or an associated 'Edit' button. ![image](../images/7a1fae0359ae0a3ed2a7de28018abba9139a1fa3c929cb819c1189db8fbe4c52-image.png) 6. On the template editing screen, scroll towards the bottom to find the **Message validity period** 7. Adjust the value in the input field to your desired TTL. You can typically specify the duration in seconds, minutes, hours, or days. ![image](../images/c2d8279519f731a7d15266581c48ccfe57e5184dc1fc24487d9754c832f379ff-image.png) > > **Tip:** Consider the nature of your message. OTPs often require a short TTL (e.g., 5-15 minutes), while appointment reminders might allow for a longer TTL (e.g., several hours before you fall back to making a phone call). > > > 8. After setting the desired validity period, ensure you save your changes by clicking **Submit**. --- ### Important Considerations * **Manage Fallbacks & User Preferences:** For the best user experience and to manage expectations, consider allowing users to select their preferred communication channel (e.g., WhatsApp, SMS, Email) during signup or within profile settings. If you *do* implement automated fallback channels, ensure your logic triggers them **only after** the specified validity period has passed (and no delivery status webhook received) to avoid sending duplicate messages across channels. * **Default TTL:** If not explicitly set, WhatsApp applies the default validity period listed in the table above. By carefully configuring the TTL for your Utility and Authentication templates, you gain finer control over message delivery timelines and can better orchestrate multi-channel communication strategies. --- ## Hangup This action should be used to hang up the incoming call. The following is an example of the JSON response you would need to provide: ```json { "clientActionId": "NumberMaskingId1", "callflow": [ { "action": "hangup" } ] } ``` Note: The Hangup action is needed only if you do not want to complete any other action. This action is not necessary if the calling user hangs up the call. --- ## iframe integration In order to provide a seamless experience to Agents, the Agent portal can be embedded in another web application. This allows Video Interaction capabilities to be easily added to another system (claim management system, customer support tool, banking application…). The following method provides a way for an agent to login and/or to join the room without manually entering his password and generating an invite. We recommend to use the following method while opening the agent console in an iframe, inside your existing application. Your users must be logged in and identified, in your application, before opening the iframe. You can load the Agent Portal in an iframe using the following URL: ```text https://video-agent.8x8.com/?user=product@8x8.com&token=eyJiJIUzI1NiME5B87.qPWXyNNDHBxLftaHarcSgm0c ``` “User” is the login of the agent who is joining the call. This agent needs to be already existing in the Video Interaction directory. “token” is the auth_token that you have created previously. **Example:** ```html ``` **Embedded Mode:** When using the iframe mechanism, you might want to display a simplified UI, more adapted to iframe size. In order to do this, you can use the URL parameter ?mode=embedded to display the again portal without the left menu and top bar. When doing so, agent won’t be able to generate new invite, hence this should be used with a token. **Example:** ```html ``` ![image](../images/4ba43d6-1580371132724.png) --- ## Inbound Messaging Apps message import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; Inbound Messages are messages sent to you by users, on one of the channels you linked with your 8x8 account. Once this happens, 8x8 sends you this message to your webhook. ### Requirements To use 8x8 inbound Messaging Apps capabilities, you need: - An account configured to use [Messaging Apps](/connect/reference/list-of-supported-chatapps-channels) product. - A webhook to indicate to us which URL 8x8 platform should forward the inbound messages addressed to you. > 📘 > > You can configure your callback using [Webhook Configuration API](/connect/reference/add-webhooks-1) > > ### Inbound message flow example 1. A user sends a message to your WhatsApp or Viber number 2. 8x8 Platform receives the message on your behalf 3. 8x8 Platform programmatically transmits the message to the callback URL configured for your webhook including all the information linked to the message. ### Retry logic In case of connection error/timeout or HTTP response code 4XX or 5XX, there will be multiple retry attempts with progressive intervals: 1, 10, 30, 90 sec. ### Webhook format > 📘 > > If you are still receiving webhooks in an older format, see the [Webhook migration guide](/connect/docs/webhook-migration-guide) for help migrating your configuration. > Request body description | Parameter name | Parameter type | Description | | --- | --- |------------------------------------------------------------------------------------------------------| | version | integer | **New in v3.** Version of the webhook payload format. Equals to `3` for this format. | | namespace | string | A generic namespace for incoming webhook.Equal to `ChatApps` for inbound Messaging Apps message. | | eventType | string | Webhook type.Equals to `inbound_message_received` for inbound Messaging Apps message. | | description | string | Human-readable description of the incoming event | | payload | object | Inbound message information, see below. | Payload object description | Parameter name | Parameter type | Description | | --- | --- | --- | | umid | uuid | Unique message ID for the inbound message | | subAccountId | string | Id of the sub-account which owns the virtual number. | | timestamp | string | UTC date and time when the message was received expressed in ISO 8601 format. | | user | object | Information about the user the message is associated with, see below. | | recipient | object | Recipient information, see below | | type | string | Inbound message type. Possible values:- `None`- `Text`- `Audio`- `Video`- `Image`- `Location`- `File`- `Carousel`- `List`- `Buttons`- `Template`- `Interactive` | | content | object | Message content | | replyToUmid | uuid | Optional context data, if this inbound message is referring to a previous inbound message (ex, quoted messages on WhatsApp). | User information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------- | | msisdn | string | **Changed in v3.** The user phone number expressed in E.164 international format. Left out for channels where users have no phone number (e.g. LINE). | | channelUserId | string | **Changed in v3.** The user's id on the channel: the **BSUID** (business-scoped user id) for WhatsApp, or the **Line user id** for Line. Only included when the channel provides a real user id. | | name | string | The WhatsApp user profile name | | username | string | **New in v3.** The WhatsApp username | When a `user` field has no value, it is simply left out of the JSON. #### Common object definitions Recipient information object description | Parameter name | Parameter Type | Description | | :------------- | :------------- | :------------------------------------------------------------------------------------------------------------------------- | | channel | string | Channel type, please see [List of supported Messaging Apps channels](/connect/reference/list-of-supported-chatapps-channels) for details. | | channelId | string | Channel user identifier. | Content information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :--------------------------------------------------------- | | text | string | Message text (for payload with type = `Text`) | | url | string | The URL of the media attachment (rich content) if any | | payload | string | Content payload (for interactive messages) | | location | object | Location object (for payload with type = `Location`) | | interactive | object | Interactive object (for payload with type = `Interactive`) | Location information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------- | | latitude | decimal | Latitude | | longitude | decimal | Longitude | Interactive information object description | Parameter name | Parameter type | Description | |----------------| --- |------------------------------------------------------------------------------------------------------------------------------| | type | string | Type of the message. Possible values:- `button_reply`- `list_reply`- `nfmReply`- `callPermissionReply` | | button\_reply | object | Button reply object. Sent when a customer clicks a button. | | list\_reply | object | List reply object. Sent when a customer selects an item from a list. | | nfmReply | object | Flow reply object. Sent when a customer completes or submits a WhatsApp Flow. Contains flow response data in `responseJson`. | | callPermissionReply | object | Call permission reply object. Sent when a customer responds to a call permission request on WhatsApp. Contains the user's permission decision and related metadata. | Button reply information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :--------------------- | | id | string | Unique ID of a button. | | title | string | Title of a button. | List reply information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------------------------------- | | id | string | Unique ID of the selected list item | | title | string | Title of the selected list item. | | description | string | Description of the selected row. | NFM reply information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------- | | responseJson | string | The raw JSON string returned from the completed Flow, containing the user's submitted data (e.g., flow token, field values). | Call permission reply information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------- | | response | string | The user's response to the call permission request. Possible values:- `accept` - User granted call permission- `reject` - User denied call permission | | isPermanent | boolean | Indicates whether the permission is permanent. `false` for temporary permissions (7 days), `true` for permanent permissions. | | expirationTimestamp | string | UTC date and time when the temporary call permission expires, expressed in ISO 8601 format. Only present when `isPermanent` is `false`. | | responseSource | string | The source of the permission. Always `user_action` - indicates the user explicitly approved or rejected the permission. Note: Automatic permissions (e.g., when a WhatsApp user initiates the call) do not trigger this webhook. | > ❗️ > > If the request you receive has a different structure from described in this document, please contact our support to activate the latest format for your account. > #### Sample payloads ##### Inbound message — WhatsApp A WhatsApp text message showing all the v3 `user` fields: the BSUID in `channelUserId`, the profile `name`, and the new `username`. ```json title="Inbound WhatsApp message webhook body (v3) — all user fields present" { "version": 3, "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": , "subAccountId": , "timestamp": "2026-06-09T10:13:20.00Z", "user": { "msisdn": , "channelUserId": , "name": , "username": }, "recipient": { "channel": "whatsapp", "channelId": }, "type": "Text", "content": { "text": "WhatsApp inbound message" } } } ``` ##### Inbound message — Viber, msisdn-only For msisdn-based channels (Viber, RCS, SMS) the `user` object contains only `msisdn`: v3 no longer fabricates a `channelUserId` from the phone number the way v2 did. ```json title="Inbound Viber message webhook body (v3) — msisdn-only user object" { "version": 3, "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": , "subAccountId": , "timestamp": "2026-07-03T01:14:03.00Z", "user": { "msisdn": }, "recipient": { "channel": "viber", "channelId": }, "type": "Text", "content": { "text": "Viber inbound message" } } } ``` ##### WhatsApp Flow response Sent when a customer completes or submits a WhatsApp Flow. The message arrives as an `Interactive` type with an `nfmReply` object carrying the user's submitted data in `responseJson`. ```json title="Inbound message webhook body — WhatsApp Flow response (nfmReply, v3)" { "version": 3, "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": , "subAccountId": , "timestamp": "2026-03-19T22:51:55.00Z", "user": { "msisdn": , "channelUserId": }, "recipient": { "channel": "whatsapp", "channelId": }, "type": "Interactive", "content": { "interactive": { "type": "nfmReply", "nfmReply": { "responseJson": "{\"flow_token\": \"\", \"optional_param1\": \"\", \"optional_param2\": \"\"}" } } } } } ``` ##### Call permission reply Sent when a customer responds to a call permission request on WhatsApp. The message arrives as an `Interactive` type with a `callPermissionReply` object containing the user's decision and, for temporary permissions, the expiration time. ```json title="Inbound message webhook body — call permission reply (callPermissionReply, v3)" { "version": 3, "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": , "subAccountId": , "timestamp": "2026-01-22T22:51:55.00Z", "user": { "msisdn": , "channelUserId": }, "recipient": { "channel": "whatsapp", "channelId": }, "type": "Interactive", "content": { "interactive": { "type": "callPermissionReply", "callPermissionReply": { "response": "accept", "isPermanent": false, "expirationTimestamp": "2026-01-23T06:51:55.00Z", "responseSource": "user_action" } } } } } ``` Request body description | Parameter name | Parameter type | Description | | --- | --- |------------------------------------------------------------------------------------------------------| | namespace | string | A generic namespace for incoming webhook.Equal to `ChatApps` for inbound Messaging Apps message. | | eventType | string | Webhook type.Equals to `inbound_message_received` for inbound Messaging Apps message. | | description | string | Human-readable description of the incoming event | | payload | object | Inbound message information, see below. | Payload object description | Parameter name | Parameter type | Description | | --- | --- | --- | | umid | uuid | Unique message ID for the inbound message | | subAccountId | string | Id of the sub-account which owns the virtual number. | | timestamp | string | UTC date and time when the message was received expressed in ISO 8601 format. | | user | object | Information about the user the message is associated with. | | recipient | object | Recipient information, see below | | type | string | Inbound message type. Possible values:- `None`- `Text`- `Audio`- `Video`- `Image`- `Location`- `File`- `Carousel`- `List`- `Buttons`- `Template`- `Interactive` | | content | object | Message content | | replyToUmid | uuid | Optional context data, if this inbound message is referring to a previous inbound message (ex, quoted messages on WhatsApp). | User information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :--------------------------------------------------------------------- | | msisdn | string | Phone number expressed in E.164 international format. | | channelUserId | string | Id of the user in the channel. Always the phone number digits (same value as `msisdn`). | | name | string | User's name in the channel. For example, username of the WhatsApp user | #### Common object definitions Recipient information object description | Parameter name | Parameter Type | Description | | :------------- | :------------- | :------------------------------------------------------------------------------------------------------------------------- | | channel | string | Channel type, please see [List of supported Messaging Apps channels](/connect/reference/list-of-supported-chatapps-channels) for details. | | channelId | string | Channel user identifier. | Content information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :--------------------------------------------------------- | | text | string | Message text (for payload with type = `Text`) | | url | string | The URL of the media attachment (rich content) if any | | payload | string | Content payload (for interactive messages) | | location | object | Location object (for payload with type = `Location`) | | interactive | object | Interactive object (for payload with type = `Interactive`) | Location information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------- | | latitude | decimal | Latitude | | longitude | decimal | Longitude | Interactive information object description | Parameter name | Parameter type | Description | |----------------| --- |------------------------------------------------------------------------------------------------------------------------------| | type | string | Type of the message. Possible values:- `button_reply`- `list_reply`- `nfmReply`- `callPermissionReply` | | button\_reply | object | Button reply object. Sent when a customer clicks a button. | | list\_reply | object | List reply object. Sent when a customer selects an item from a list. | | nfmReply | object | Flow reply object. Sent when a customer completes or submits a WhatsApp Flow. Contains flow response data in `responseJson`. | | callPermissionReply | object | Call permission reply object. Sent when a customer responds to a call permission request on WhatsApp. Contains the user's permission decision and related metadata. | Button reply information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :--------------------- | | id | string | Unique ID of a button. | | title | string | Title of a button. | List reply information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------------------------------- | | id | string | Unique ID of the selected list item | | title | string | Title of the selected list item. | | description | string | Description of the selected row. | NFM reply information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------- | | responseJson | string | The raw JSON string returned from the completed Flow, containing the user's submitted data (e.g., flow token, field values). | Call permission reply information object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :---------- | | response | string | The user's response to the call permission request. Possible values:- `accept` - User granted call permission- `reject` - User denied call permission | | isPermanent | boolean | Indicates whether the permission is permanent. `false` for temporary permissions (7 days), `true` for permanent permissions. | | expirationTimestamp | string | UTC date and time when the temporary call permission expires, expressed in ISO 8601 format. Only present when `isPermanent` is `false`. | | responseSource | string | The source of the permission. Always `user_action` - indicates the user explicitly approved or rejected the permission. Note: Automatic permissions (e.g., when a WhatsApp user initiates the call) do not trigger this webhook. | > ❗️ > > If the request you receive has a different structure from described in this document, please contact our support to activate the latest format for your account. > #### Sample payloads ##### Inbound message — WhatsApp A WhatsApp text message in the v2 format: no `version` field, and `channelUserId` repeats the phone number. ```json title="Inbound WhatsApp message webhook body (v2)" { "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": , "subAccountId": , "timestamp": "2026-01-01T14:34:56.017Z", "user": { "msisdn": , "channelUserId": }, "recipient": { "channel": "whatsapp", "channelId": }, "type": "Text", "content": { "text": "Test message" }, "replyToUmid": } } ``` ##### WhatsApp Flow response Sent when a customer completes or submits a WhatsApp Flow. The message arrives as an `Interactive` type with an `nfmReply` object carrying the user's submitted data in `responseJson`. ```json title="Inbound message webhook body — WhatsApp Flow response (nfmReply, v2)" { "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": , "subAccountId": , "timestamp": "2026-03-19T22:51:55.00Z", "user": { "msisdn": , "channelUserId": }, "recipient": { "channel": "whatsapp", "channelId": }, "type": "Interactive", "content": { "interactive": { "type": "nfmReply", "nfmReply": { "responseJson": "{\"flow_token\": \"\", \"optional_param1\": \"\", \"optional_param2\": \"\"}" } } } } } ``` ##### Call permission reply Sent when a customer responds to a call permission request on WhatsApp. The message arrives as an `Interactive` type with a `callPermissionReply` object containing the user's decision and, for temporary permissions, the expiration time. ```json title="Inbound message webhook body — call permission reply (callPermissionReply, v2)" { "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": , "subAccountId": , "timestamp": "2026-01-22T22:51:55.00Z", "user": { "msisdn": , "channelUserId": }, "recipient": { "channel": "whatsapp", "channelId": }, "type": "Interactive", "content": { "interactive": { "type": "callPermissionReply", "callPermissionReply": { "response": "accept", "isPermanent": false, "expirationTimestamp": "2026-01-23T06:51:55.00Z", "responseSource": "user_action" } } } } } ``` --- ## Inbound SMS 8x8 SMS API exposes a webhook mechanism to let you receive Inbound SMS (SMS sent to your virtual numbers) on a callback URL of your choice. Whenever you receive inbound SMS, the 8x8 SMS platform sends a `POST` request to the endpoint of your choice with a `JSON` body containing the inbound SMS and the associated data. ### Requirements To use inbound SMS capabilities, liaise with your account manager to activate the following: - A **virtual mobile number** where your user will send their SMS - An **inbound SMS callback URL**: set the URL, where 8x8 platform should forward the inbound messages addressed to your virtual number > 📘 > > You can configure your callback using [Webhooks Configuration API](/connect/reference/get-webhooks-2) > > ### Inbound SMS flow 1. A user sends an SMS to one of your virtual numbers 2. 8x8 receives the SMS on your behalf 3. 8x8 programmatically sends the SMS to the callback URL configured for your virtual number using `POST` request. 4. The `POST` request body, in the `JSON` format contains the SMS body and all associated data (`UMID`, source, destination, encoding, timestamp) ### Retry logic In case of connection error/timeout or HTTP response code 4XX or 5XX, there will be multiple retry attempts with progressive intervals: 1, 10, 30, 90 sec. ### Webhook format Request body description | Parameter name | Parameter type | Description | | --- | --- |------------------------------------------------------------------------------| | namespace | string | A generic namespace for incoming webhook.Equal to `SMS` for inbound SMS. | | eventType | string | Webhook type. Equals to `inbound_message_received` for inbound SMS. | | description | string | Human-readable description of the incoming event | | payload | object | Inbound message information, see below. | Payload object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :------------------------------------------------------------------------------------------------------------ | | umid | uuid | Unique message ID for the inbound message | | subAccountId | string | Id of the sub-account which owns the virtual number. | | timestamp | string | UTC date and time when the message was received expressed in ISO 8601 format. | | source | string | Originating address of the SMS (sender number) | | destination | string | The destination address of the SMS (virtual number) | | body | string | Content of the SMS | | encoding | string | The encoding used in the SMS body (GSM7 / UCS2) | | smsCount | integer | Number of SMS segment in the message | | price | object | Price information of the message, please see [Price object reference](/connect/reference/price-object-reference) for details | > ❗️ > > If the request you receive has a different structure from described in this document, please contact our support to activate the latest format for your account. ### Sample inbound SMS callback body ```json Inbound SMS webhook body { "namespace": "SMS", "eventType": "inbound_message_received", "description": "SMS inbound message", "payload": { "umid": "9e09ac86-bd74-5465-851d-1eb5a5fdbb9a", "subAccountId": "SubAccount-1", "timestamp": "2016-01-01T14:34:56.017Z", "source": "+6581968289", "destination": "+4534735477", "body": "Test MO message", "encoding": "GSM7", "smsCount": 2, "price": { "total": 0.0446592, "perSms": 0.0446592, "currency": "EUR" } } } ``` --- ## Integrations Overview ## Intergrations - Customer Support | Integration | Link to Integration | | --- | --- | | **Salesforce Live Agent** - Integrate Chat Apps messaging from within Salesforce | [Link to Integration](/connect/docs/salesforce-live-agent) | | **Zendesk Support** - Send and receive notifications about ticket updates from different Chat apps channels**Zendesk Notifcation Webhook**- Leverage the Zendesk triggers system and 8x8 API to send SMS when an event occurs on Zendesk | [Link to Integration](/connect/docs/zendesk-support)[Link to Integration](/connect/docs/zendesk-notifications-targets) | | **Freshdesk** - Integrate SMS or Chat Apps messaging within Feshdesk's ticketing | [Link to Integration](/connect/docs/freshdesk) | ## Integrations Automated Workflows | Integration | Link to Integration | | --- | --- | | **Zapier** - Connect your apps on Zapier | [Link to Integration](/connect/docs/zapier) | | **Workato** - Integrate SMS messaging with Workto's workflow | [Link to Integration](/connect/docs/workato) | | **Apple's Shortcuts**- Send SIngle SMS API request using the Apple's Shortcut App | [Link to Integration](/connect/docs/apples-shortcuts) | | **Make** - Connect your apps on Make | [Link to Integration](/connect/docs/makecom-1) | | **Cognigy** - Connect to Cognigy, a Conversational AI platform. | [Link to Integration](/connect/docs/cognigy) | ## Security | Integration | Link to Integration | | --- | --- | | **Auth0** - Identity and authentication service that can integrate 8x8 SMS to send one-time passwords for 2FA | [Link to Integration](/connect/docs/auth0) | | **Okta** - Identity and access management platform that can integrate 8x8 SMS to send one-time passwords (OTP) for 2FA via inline hooks | [Link to Integration](/connect/docs/okta) | ## Logistics | Integration | Link to Integration | | --- | --- | | **Detrack** - Easy to use tracking service with automatic real-time notifications that send SMS notification based on event triggers | [Link to Integration](/connect/docs/detrack) | ## Mobile and Web Marketing | Integration | Link to Integration | | --- | --- | | **CleverTap** - Add SMS or Chat Apps messaging in your mobile marketing campaigns | [Link to Integration](/connect/docs/clevertap) | | **Braze** - Engage more customers with SMS or Chat Apps with Braze | [Link to Integration](/connect/docs/braze) | | **Adobe Campaigns** - allows you to launch, measure, and automate campaigns across every channel. Use 8x8 SMS functionality into one other communication channel | [Link to Integration](/connect/docs/adobe-campaigns) | | **Oracle Responsys** - helps you manage, personalize, and orchestrate interactions across all channels to deliver timely, helpful messages in the moments that matter | [Link to Integration](/connect/docs/oracle-responsys) | | **MoEngage** - allows you to launch, measure, and automate campaigns via Whatsapp. | [Link to Integration](/connect/docs/moengage) | --- ## IP address list ## Messaging Products Webhooks > 🚧 IP Address List > > The following IP addresses only apply to **messaging related products (SMS API, Messaging Apps API, Mobile Verification API, Automation API, Number Lookup API)** If you need to restrict inbound traffic to your webhook endpoint, please allow requests originating from the following outbound IP addresses: | Platform Region | IP Addresses | | --------------- | ----------------------------------------------------------- | | Asia Pacific | 52.220.117.120, 52.220.109.9, 18.136.13.204 | | Indonesia | 34.101.157.207, 34.101.215.77, 34.34.217.244, 34.101.109.67 | | Europe | 34.142.20.188, 34.142.64.178, 34.39.69.103, 34.39.114.155 | | North America | 50.112.250.190, 100.21.170.42, 52.10.10.63 | *This list was updated on: October 13th, 2025.* ## Programmable Voice For detailed Voice IP address information, see the [Voice IP Addresses](/connect/docs/voice/voice-ip-addresses) page. If you need to restrict inbound traffic to your webhook endpoints, please allow requests originating from the following outbound IP addresses: | Platform Region | IP Addresses | | --------------- | ------------ | | Asia Pacific (Singapore) | 18.140.80.2, 52.220.253.234, 54.255.116.8, 52.74.232.241 | These IPs are used for webhook deliveries from Voice products: - **Number Masking** - Voice Call Action webhook, Voice Session Summary webhook, Voice Call Status webhook, Voice Recording Uploaded webhook, Virtual Number Updated webhook - **Voice Messaging** - Voice Session Summary webhook - **Interactive Voice Response** - Voice Call Action webhook, Voice Session Summary webhook *This list was updated on: January 1st, 2026.* --- ## LINE Account Provisioning and Onboarding This guide covers the complete path from "we want LINE" to "we are sending production messages". It sets out the three provisioning routes, what you have to supply, the lead times to plan for, and how support works after go-live. Read [The LINE Ecosystem and Account Model](./concepts-fundamentals.md) first, because the vocabulary there is what the provisioning form asks you for. ## Choose Your Provisioning Path There are three genuinely different journeys, depending on whether you already own a LINE Official Account and, if so, who currently manages it. Identify yours before you start, because the work, the owner, and the lead time all differ. | | **Path A: New OA provisioned by 8x8** | **Path B: Bring your own OA** | **Path C: Migrate an OA from another provider** | |---|---|---|---| | **Starting point** | You have no LINE Official Account | You already have an OA with the Messaging API available to you | You have an OA that another provider or agency manages | | **Who creates the OA** | 8x8, as a LINE-registered Agency (AGP) partner, on your behalf | You already did | LINE, via its provider migration process | | **What you submit** | The provisioning information sheet, plus a profile image | Channel ID and Channel Secret from LINE Official Account Manager | A migration request, qualified with 8x8 first | | **Who administers the OA afterwards** | 8x8 manages the account as your agency partner | You retain administration in LINE Official Account Manager | Determined as part of the migration | | **Main lead-time driver** | LINE verification, **14 business days** | Your own console access | LINE's migration process, about **25 business days** | | **Documented in** | [What You Need to Provide](#what-you-need-to-provide) and [The Onboarding Sequence](#the-onboarding-sequence) | [Connecting Your OA to 8x8](#connecting-your-oa-to-8x8) | The note below | ### Path A: New OA provisioned by 8x8 8x8 is a LINE-registered Agency (AGP) partner and can create and manage a LINE Official Account for you. You complete one information sheet per Official Account, 8x8 submits the provisioning request to LINE, and LINE runs its verification. This is the path most new customers take, and it is the one the rest of this page is organised around. ### Path B: Bring your own OA If you already have a LINE Official Account, you keep it. Enable the Messaging API on the account, retrieve the channel credentials from LINE Official Account Manager, and hand them to 8x8. See [Connecting Your OA to 8x8](#connecting-your-oa-to-8x8). ### Path C: Migrate an OA from another provider > **Important:** If your Official Account is currently managed by another provider or agency, moving it to 8x8 means going through LINE's provider migration process. LINE charges a fee for migration and the process takes about **25 business days**. Treat this as a qualification question, not a default: in some cases provisioning a new Official Account is faster and cheaper than migrating an existing one. Ask your 8x8 account manager for the current LINE migration fee before committing. ## What You Need to Provide For Path A, LINE requires a defined set of information for every Official Account, covering the account's name and handle, its category, its service language and country, its launch date, and its profile image. Contact your 8x8 account manager to obtain the provisioning information sheet and submit the required details. One field carries a cost consequence rather than a formatting one: setting the account's **push notification default** to On applies a **50% surcharge**. Confirm the cost impact with your account manager before selecting it. Two rules worth restating, because they shape commercial planning: - **One form per Official Account.** Each account requires its own submission. - **Each customer needs their own Official Account.** 8x8 does not send from a shared Official Account, so provisioning is per customer, not per platform. > **Note** > > The **OA ID** field on the form is the account's Premium ID, purchased from LINE. For what a Premium ID is and why a customer would want one, see [Why Premium ID Is Meaningful](./concepts-fundamentals.md#why-premium-id-is-meaningful). LINE's own [Premium ID Terms of Use](https://terms2.line.me/official_account_premiumid_terms_oth) carry the character limits, the annual fee, and the renewal rules. ## The Onboarding Sequence Path A runs through eight steps. The owner changes as it progresses, so track it as a shared plan rather than a handoff. 1. **Intake.** You submit the provisioning information sheet, and email the profile image separately. - *Owner:* you. 2. **Submission validated and provisioning request raised.** 8x8 checks the submission for completeness and consistency against your business registration, then raises the provisioning request. - *Owner:* 8x8 Operations. 3. **Provisioning with LINE.** 8x8 submits the request to LINE as your Agency (AGP) partner. This creates the Official Account and applies the Premium ID and Verified Account application if you ordered them. - *Owner:* 8x8 Operations, acting as your LINE agency. 4. **LINE verification.** LINE reviews and verifies the account. This is the long pole in the whole process. - *Owner:* LINE. *Lead time:* **14 business days.** 5. **Channel connection and credential issuance.** 8x8 links the Official Account to your 8x8 sub-account and issues your API credentials and webhook URL. - *Owner:* 8x8 Operations and Platform Engineering. 6. **API integration.** Your developers integrate: send and receive Text, Image, Video, Audio, and Location, and register your callback URL for the inbound message webhook. - *Owner:* your development team. See [Getting Started](./getting-started.md). 7. **Converse setup (optional).** If your plan includes agent-side handling of inbound LINE conversations, 8x8 provisions seats and inbox routing at this point. - *Owner:* 8x8 Customer Success and you. 8. **Go-live and monitoring.** First production message validated, delivery monitoring in place. - *Owner:* 8x8 and you. > **Note** > > Steps 6 and 4 can and should overlap. Nothing in the API integration work depends on LINE having finished verifying the account, so start intake early in the sales cycle and let verification run in parallel with development. ## Timeline Expectations - **Plan for roughly 3 to 4 weeks** from intake to first production message. - **Most of that is waiting on LINE, not on 8x8.** The **14-business-day** LINE verification lead time dominates the schedule. - **Start intake early.** Because verification runs independently of your development work, submitting the information sheet early is the single most effective way to shorten the calendar. ### Two different published durations, measuring two different things You will encounter two numbers. They are not in conflict, because they do not measure the same thing: | Duration | What it measures | Who states it | |---|---|---| | **14 business days** | The end-to-end lead time for an 8x8-managed Official Account provisioning request, from submission of materials to a provisioned account | 8x8, based on LINE's Official Account provisioning process | | **About 10 business days** | LINE's own Verified Account review, from application to completion | LINE, in its Official Account help centre | Quote the **14 business days** figure when setting expectations for an 8x8-provisioned account. Quote the **10 business days** figure only when the question is specifically about LINE's Verified Account review. See [Why Verified Account Matters](./concepts-fundamentals.md#why-verified-account-matters). ## Connecting Your OA to 8x8 This section applies to Path B, and to the credential handover at step 5 of Path A. ### What 8x8 Needs From You (Path B) 1. In [LINE Official Account Manager](https://manager.line.biz/), go to **Settings** > **Messaging API** and enable it. 2. On that same **Messaging API** settings page, retrieve: - **Channel ID:** the unique identifier for your LINE channel. - **Channel Secret:** the secret key used to generate access tokens. Retrieving it requires Admin privileges on the channel. 3. Provide the Channel ID and Channel Secret to 8x8. Contact your account manager or [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) to have the LINE channel configured on your sub-account. ![LINE Official Account Manager, Settings > Messaging API, showing Channel ID, Channel Secret, and Webhook URL](./images/LINE%20Official%20Account%20Manager%20-%20Messaging%20API%20Settings.jpg) > **Note** > > The same Channel ID and Channel Secret are also visible from the LINE Developers Console, which LINE Official Account Manager links out to for more advanced channel settings. For what 8x8 needs, the Messaging API settings page above is enough. ### What 8x8 Configures For You - **A new sub-account.** LINE requires a new sub-account ID. It **cannot** be an existing SMS sub-account. - **The channel itself.** 8x8 configures your LINE channel on that sub-account. - **Any fallback chain.** The 8x8 team sets up your account's multi-channel fallback: which channels are used, in what order, and how long to wait before triggering the next one. The `line` channel reports `Accepted` and `Sent` delivery statuses, which the fallback chain can use. See [Delivery Receipts](./loa-webhook.md#delivery-receipts). - **Your webhook URL.** 8x8 issues the URL that LINE should post to. Contact [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) to obtain the correct webhook URL for your account. Once the channel is live, generate your API key in the [8x8 Connect portal](https://connect.8x8.com/). See [Authentication](./getting-started.md#authentication). ## Configuring Your Webhook Two separate webhook configurations are involved, and it is easy to conflate them. One points LINE at 8x8. The other points 8x8 at you. **1. Point LINE at 8x8.** In [LINE Official Account Manager](https://manager.line.biz/), go to **Settings** > **Messaging API**, set the **Webhook URL** field to the URL provided by 8x8, and click **Save**. ![LINE Official Account Manager, Settings > Messaging API, showing the Webhook URL field and Save button](./images/LINE%20Official%20Account%20Manager%20-%20Messaging%20API%20Settings.jpg) **2. Point 8x8 at your application.** Register your own callback URL with 8x8 using the [Webhooks Configuration API](/connect/reference/add-webhooks-1). One callback URL serves every Messaging Apps channel on the account, and the `channel` field in each payload tells you which channel a message came from. See [Configuring Your Webhook](./loa-webhook.md#configuring-your-webhook). ## After Go-Live ### What lives where | Task | Where you do it | |---|---| | Sending and receiving messages | 8x8 Messaging Apps API | | Inbound message webhooks, and LON delivery receipts | Your callback URL, registered with 8x8 | | Message volume reporting across channels | [Messaging Apps Analytics](/connect/docs/messaging-apps) in 8x8 Connect | | API key generation and rotation | [8x8 Connect portal](https://connect.8x8.com/) | | Account profile, display name, and profile image | LINE Official Account Manager | | Subscription plan and Premium ID purchase | LINE Official Account Manager | | Search visibility toggle | LINE Official Account Manager | | Administrator permissions | LINE Official Account Manager | | LINE's own account statistics (Insights) | LINE Official Account Manager | ### Escalation Account-level LINE issues, including verification problems, account suspension, and package changes, are escalated through the 8x8 agency relationship with LINE rather than raised by you directly with LINE. Raise them with 8x8 support and 8x8 escalates on your behalf. Message delivery and API issues follow the standard 8x8 CPaaS support process. Note that the `line` channel reports `Accepted` and `Sent` but not `Delivered` or `Read`. Build your monitoring around these statuses and the synchronous send response. See [Delivery Receipts](./loa-webhook.md#delivery-receipts). ## Next Steps **For Business Users:** - [The LINE Ecosystem and Account Model](./concepts-fundamentals.md) - What Verified Account and Premium ID actually mean before you order them - [Governance, Security and Compliance](./governance-security.md) - LINE's account rules and what happens if you break them **For Developers:** - [Getting Started with LINE over the 8x8 API](./getting-started.md) - Authentication, base URLs, and your first send - [LINE Official Account Webhooks](./loa-webhook.md) - Inbound messages, delivery receipts, and retry behaviour **Important Concepts:** - [Identifying a LINE Recipient](./loa-messaging.md#identifying-a-line-recipient) - Why you cannot send to a phone number on this channel --- ## The LINE Ecosystem and Account Model This page explains the LINE side of the account model: the console that sits above your account, what a LINE Official Account is and how one account can serve both LINE products on 8x8, the channel and credential objects that sit underneath it, the account tiers LINE offers and what each one unlocks, and the two decisions customers ask about most, applying for a Verified Account and buying a Premium ID. Read it before you decide what to order, because these are the rules that most often surprise a customer mid-onboarding. ## LINE Official Account Manager **LINE Official Account Manager** is LINE's own console for an Official Account, available as a web application and as a mobile app. It sits at the top of the hierarchy: even when 8x8 drives all message traffic through the API, this console remains the place where account-level settings live. What you continue to do in LINE Official Account Manager: - **Manage the account profile,** including the profile visibility setting that controls whether a Verified Account appears in search results. - **Purchase and change the subscription plan,** purchase an OA Chat package subscription, and purchase a Premium ID. - **Register and change the payment method.** - **Manage administrator permissions.** - **View Insights,** LINE's own account statistics. ## The LINE Official Account A **LINE Official Account (OA)** is the account entity that everything else hangs off. It is the identity users see, the thing they add as a friend, the thing an account type and a Premium ID attach to, and the thing 8x8 connects a channel to. One OA, one business identity on LINE. A single LINE Official Account can be used for both LINE products on 8x8 at the same time: two-way LINE Official Account (LOA) messaging and one-way LINE Official Notification (LON). You do not need a second OA for LON. What you do need is **two separate 8x8 sub-accounts**, one per product. The reason is how each product addresses a recipient: - **LOA addresses recipients by `channelUserId`,** the opaque LINE user ID. - **LON addresses recipients by `msisdn`,** a phone number. Because the two products address a recipient differently, 8x8 provisions one sub-account per product, each with its own API key. See [Sub-accounts and Authentication](./loa-messaging.md#sub-accounts-and-authentication) for the LOA side and [LINE Official Notification (LON)](./official-notification-lon.md) for LON's. > **Key Takeaway:** One LINE Official Account, two 8x8 sub-accounts. Ordering LON does not mean ordering a second Official Account, but it does mean a second sub-account and a second API key. ### Channel Type Values Two channel type values in the 8x8 Messaging Apps platform are LINE products. The value identifies the channel in the webhooks that product produces, and it determines which endpoint you send on. The two products produce different webhooks, so each value surfaces in a different place. | Channel type value | Product | Send endpoint | Appears in | |---|---|---|---| | `line` | LINE Official Account | `POST /api/v1/subaccounts/{subAccountId}/messages` | `recipient.channel` on inbound messages and `channel` on delivery receipts. Reports Accepted and Sent | | `LineNotification` | LINE Official Notification | `POST /api/v1/subaccounts/{subAccountId}/lon` | `channel` on delivery receipts only. This channel has no inbound direction | Note the casing. `line` is lowercase, `LineNotification` is camel case with a capital L. In the separate `channels` fallback override array on the send request, the same channel is spelled `Line` with a capital L. Use each value exactly as documented for the field you are populating. ### The Technical Building Blocks Underneath the Official Account sit the LINE objects that a Messaging API integration is built on. 8x8 operates the API path for you, so you will meet most of them in LINE's own console and during provisioning rather than in your own code, but they are the vocabulary LINE's documentation uses. 1. **LINE Messaging API channel:** A channel is a communication path used to access features of the LINE Platform. LINE offers several channel types, including the Messaging API channel, the LINE Login channel, and the LINE MINI App channel. Enabling the Messaging API on your Official Account creates a Messaging API channel, and that channel is what 8x8 connects to. 2. **Channel secret:** A private key known only to the LINE Platform and the developer, found on the channel's **Basic settings** tab in the LINE Developers Console. It is the hash key LINE uses to sign webhooks. Retrieving or reissuing it requires Admin privileges on the channel. 3. **Channel access token:** An opaque string that proves an application is permitted to use the channel. 4. **Provider:** The LINE entity that owns your channels. This matters for identity: LINE issues a **different** user ID for the same person under a different provider. Under the same provider the user ID is identical across channel types. LINE issues four kinds of channel access token, with different validity periods and per-channel issue limits: | Type | Validity period | Number of issues per channel | | :--- | :--- | :--- | | **Channel access token with a user-specified expiration** | Up to 30 days | 30 | | **Stateless channel access token** | 15 minutes | Limitless | | **Short-lived channel access token** | 30 days | 30 | | **Long-lived channel access token** | Indefinite | 1 | > **Note** > > You do not manage channel access tokens yourself when sending through 8x8. 8x8 authenticates to LINE on your behalf, and your application authenticates to 8x8 with an 8x8 API key. Channel access tokens matter only if you also operate your own LINE bot server directly against LINE's API. ## Account Types LINE's Official Account Guidelines describe two account types that a customer can apply for, plus a third tier that LINE assigns at its own discretion. | Account type | Who can use it | Review required | In-app search | Badge | |---|---|---|---|---| | **Unverified Account** | Any company, organization, or individual | No | Does not appear in LINE in-app search results | No | | **Verified Account** | Only customers who pass LINE's screening | Yes | Appears in LINE in-app search results | Verified account badge | | **Premium Account** | Only customers who pass a screening process instituted by LINE | Yes | Not stated by LINE | Not stated by LINE | - **Unverified Account:** Available for use by any company, organization, or individual. These accounts have not been reviewed. LINE states that they do not appear in search results either on the internet or in the LINE app. - **Verified Account:** An account that has passed LINE's review process. Once reviewed, it is issued a Verified account badge and appears in LINE in-app search results. - **Premium Account:** LINE may set criteria for making an account into a Premium Account. LINE explicitly states that it **has no responsibility to disclose the criteria**. You cannot apply for a Premium Account the way you apply for a Verified Account. > **Note** > > Some LINE Messaging API features are gated on account type. The endpoints that return the user IDs of all your friends, and of all members of a group or multi-person chat, are available only to Verified or Premium accounts. Neither is exposed through the 8x8 API. ## Why Verified Account Matters A Verified Account is the difference between an account customers can find and one they cannot. It carries the Verified account badge, and it appears in LINE in-app search results, which an Unverified Account never does regardless of any setting. If discovery inside the LINE app is part of your acquisition plan, verification is the mechanism. You cannot create a Verified Account directly. LINE requires you to create an Unverified Account first and then apply for verification. Accounts created in the LINE Official Account app are Unverified, and LINE states that Verified accounts cannot be created from that app at all. - **Where it is available:** Review applications are currently accepted **only for Japan, Taiwan, and Thailand**. LINE states that Verified accounts are not being issued for other countries or regions at this time. - **How long it takes:** From application to completion, LINE states the review process normally takes **about 10 business days**. - **Identity verification:** To verify your identity, LINE contacts you by phone or at the email address registered at the time of application. LINE states that the call will be recorded. - **No status visibility:** LINE states that it cannot provide details about the status of a pending review, and cannot reply to related inquiries. - **No explanation of the outcome:** LINE bears no obligation to explain the results of the screening process or the reasons behind any decision. - **Industry-based refusal:** Beyond the published rules, LINE may refuse to open a Verified Account on the grounds of the customer's industry or lines of business, at its sole discretion. ### Verified Account name rules LINE requires Verified Account names to follow four rules. Names that infringe them may be subject to a request for amendment from LINE. 1. Names **must** include the official name of the company, organization, or self-employed person, or the official name of the product or service they provide. 2. Names **must not** imply the existence of companies, organizations, self-employed persons, products, or services which do not exist. 3. Names **must not** include text strings which are non-factual or which may lead to erroneous inferences. 4. Names **must not** imply that they pertain to products or services offered by LINE or its affiliated companies. > **Important:** LINE states that, in principle, it does **not** accept account name changes for Verified Accounts. If the company or service name has genuinely changed and you wish to change the account name, the account is subject to a **separate screening process**. ## Why Premium ID Is Meaningful A **Premium ID** is a LINE Official Account ID of your own choosing, purchased from LINE to replace the basic ID that LINE assigns automatically. It is the customer-facing `@` handle for the account, so it is what a customer types, reads on a poster, or hears in a call centre script. A handle that matches your brand is easier to recognise, easier to remember, and easier to promote off-platform than a LINE-assigned string. On the 8x8 provisioning form, this is the **OA ID** field. LINE sets the commercial and character-set terms for a Premium ID, including the annual fee, the length and character limits, the renewal and refund rules, and the countries where it is not available. Those terms change, so take them from LINE's own contract rather than from this page: see [LINE Official Account Premium ID Terms of Use](https://terms2.line.me/official_account_premiumid_terms_oth). ## Account Limits per Business ID LINE allows up to **100 accounts** per Business ID, whether Verified or Unverified. Plan your Business ID structure with that ceiling in mind if you expect to run many Official Accounts. ## Account Deletion Deleting a LINE Official Account is not reversible in any practical sense. LINE states the following consequences, and they are worth reading before anyone clicks the button: - **You will not be able to use any features other than Insights and billing** once the account is deleted. - **All account information, including statistics, is permanently erased 30 days after deletion.** - **All of your friends are deleted.** - **There is no refund** if you delete the account in the middle of a month. - **Additional messages sent before deletion are still billed** on your next billing date. - **An iOS-purchased Premium ID subscription is not cancelled automatically.** You must cancel the subscription separately on the App Store. ## Related Resources **8x8 Documentation:** - [Account Provisioning and Onboarding](./account-provisioning-onboarding.md) - The three provisioning paths, what you submit, and the lead times - [Governance, Security and Compliance](./governance-security.md) - LINE's prohibited content, enforcement model, and regional availability - [LINE Official Account: Behaviour, Messages, and Constraints](./loa-messaging.md) - How the two-way channel addresses a recipient, how LINE counts what you send, and a complete request body per supported type - [LINE Official Notification (LON)](./official-notification-lon.md) - The second product the same Official Account can serve **LINE's Own References:** - [LINE Official Account Guidelines](https://terms2.line.me/official_account_guideline_th?lang=en) - Account types, screening criteria, prohibited activities, and enforcement - [LINE Official Account Premium ID Terms of Use](https://terms2.line.me/official_account_premiumid_terms_oth) - Character limits, term, fees, and refund policy - [LINE Official Account Help Center (Thailand)](https://help2.line.me/official_account_th/web/categoryId/200000074/pc?lang=en) - Account types, account creation, review process, plans, and settings - [LINE Official Account Manager](https://manager.line.biz/) - The console itself - [LINE Official Account (Thailand)](https://lineforbusiness.com/th/service/line-oa-features/broadcast-message) - Thailand plans and pricing, published by LINE --- ## Getting Started with LINE over the 8x8 API This guide takes a developer who has been handed working credentials from nothing to a first LINE message accepted by the platform. It starts with what both LINE products share, authentication and the regional base URLs, then splits: one section for the two-way **LINE Official Account (LOA)** channel, and one for the one-way **LINE Official Notification (LON)** product. They use different endpoints and address recipients differently, so read the section for the product you are integrating. If you are still waiting on an Official Account, see [Account Provisioning and Onboarding](./account-provisioning-onboarding.md) instead. ## Prerequisites & Checklist Before you begin, ensure you have the following: - **An 8x8 account with Messaging Apps enabled.** Contact your account manager to confirm the product is enabled on your account. Sign up at [connect.8x8.com](https://connect.8x8.com) if you do not have an account yet. - **A LINE sub-account.** LINE requires a **new sub-account ID**. It cannot be an existing SMS sub-account. - **An API key** generated in the 8x8 Connect portal, scoped to that sub-account. - **A LINE Official Account linked to the sub-account.** 8x8 configures the channel for you. See [Connecting Your OA to 8x8](./account-provisioning-onboarding.md#connecting-your-oa-to-8x8). - **A registered webhook.** On the LOA channel you cannot obtain a recipient's `channelUserId` without one, so this is a hard prerequisite rather than a later step. See [Configuring Your Webhook](./loa-webhook.md#configuring-your-webhook). - **A separate sub-account per product.** LOA and LON have different sending behaviour, so set up each in a different 8x8 sub-account. ## Authentication The 8x8 Messaging Apps API accepts an **ApiKey Bearer Token** authentication method. Generate tokens from your customer portal at [https://connect.8x8.com/](https://connect.8x8.com/), then include the following header in every request: ```http Authorization: Bearer {apiKey} ``` Replace `{apiKey}` with the key generated from the customer portal. > **Important:** The LINE Official Account sub-account token and the LINE Official Notification sub-account token are **separate**. A key that authorises a send on one will not authorise a send on the other. ## Base URLs Use the base URL that matches the platform region your account is provisioned in. Sending to the wrong region will not reach your sub-account. Both LOA and LON use these same base URLs. | API Region | Base URL | | :--- | :--- | | Asia (default) | `https://chatapps.8x8.com` | | Europe | `https://chatapps.8x8.uk` | | North America | `https://chatapps.us.8x8.com` | | Indonesia | `https://chatapps.8x8.id` | For more information on platform regions, see [Platform Deployment Regions](/connect/docs/platform-deployment-regions#api-endpoints-and-platform-region). ## Sending on LINE Official Account (LOA) This is the two-way `line` channel. It carries Text, Image, Video, Audio, and Location messages, and it addresses recipients by their LINE user ID. ### The Send Endpoint One endpoint sends every LINE Official Account message type. The `type` field selects the content type, and only the `content` object changes with it. **Endpoint:** ```json POST https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/messages ``` **Path Parameters:** - `{subAccountId}`: The sub-account that owns your LINE channel. 3 to 50 characters, restricted to letters, digits, and the characters `-`, `.`, `_`, and `&`. The `user`, `type`, and `content` properties are required in the request body. ### Getting the channelUserId First You cannot send a LINE message to a phone number, and you cannot look a LINE user up. The identifier you need is issued by LINE and reaches you only through an inbound event. 1. A LINE user adds your LINE Official Account as a friend, or sends it a message. 2. LINE notifies 8x8, and 8x8 forwards an inbound message webhook to your registered callback URL. 3. Read the LINE user ID from `payload.user.channelUserId` in that webhook. 4. Store it. That value is what you put in `user.channelUserId` when you send. The format is `U[0-9a-f]{32}`: the letter `U` followed by exactly 32 lowercase hexadecimal characters. The same person has a **different** user ID under a different LINE provider, so an ID obtained through another integration is not usable in yours. > **Key Takeaway:** Until a user has interacted with your Official Account, you have no way to address them on the `line` channel. Design your onboarding around capturing `channelUserId` on first contact. See [Identifying a LINE Recipient](./loa-messaging.md#identifying-a-line-recipient). ### Send Your First Message The minimum viable LINE send is a text message to one user. **Sample JSON Payload:** ```json { "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "type": "Text", "content": { "text": "Hello from 8x8 Messaging API" }, "clientMessageId": "" } ``` **Key Fields:** - `user.channelUserId` (required): The LINE user ID of the recipient. Use this instead of `msisdn` when sending to a LINE user on the LINE Official Account channel. - `type` (required): The content type. Written in Title Case in every 8x8 LINE example: `Text`, `Image`, `Video`, `Audio`, `Location`. - `content.text` (required for `Text`): The message body. - `clientMessageId` (optional): Your own unique reference for the message, maximum 50 characters. It is echoed back in the send response and in delivery receipts. > **Note** > > LINE counts a text message in UTF-16 code units, with a maximum of 5,000 characters, and emoji count as more than one character. Per-type payloads and their full constraints are in [LINE Official Account: Behaviour, Messages, and Constraints](./loa-messaging.md). ### The Response A successful send returns `200` with the message identifier and its initial status. **Response:** ```json { "umid": "", "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "clientMessageId": "", "status": { "state": "queued", "timestamp": "2026-07-29T08:19:45.99Z" } } ``` **Key Fields:** - `umid`: The unique message ID (a GUID) generated by the 8x8 platform on submission. It is the platform's identifier for this message, so persist it against your own record. - `user.channelUserId`: Echoes the recipient you submitted. - `status.state`: `queued` on a successful submission, meaning the request was accepted and queued for processing. It is not a delivery confirmation. - `status.timestamp`: UTC, ISO 8601. - `clientMessageId`: Your own reference, echoed back. > **Important:** The `line` channel reports **Accepted** and **Sent** delivery statuses but does not report **Delivered** or **Read**. After this synchronous response, delivery receipts will arrive at your callback URL as the message progresses. See [Delivery Receipts](./loa-webhook.md#delivery-receipts). #### Error responses Failures at submission time come back synchronously with an error body: | HTTP status | Meaning | Example body | |---|---|---| | `400` | Bad request. A parameter is missing or invalid | `{"code": 1002, "message": "Invalid MSISDN format (not E.164 international number)", "errorId": "...", "timestamp": "..."}` | | `401` | The request was not authenticated | `{"code": 1200, "message": "Request was not authenticated properly", "errorId": "...", "timestamp": "..."}` | | `500` | Internal server error | `{"code": 2000, "message": "Internal server error", "errorId": "...", "timestamp": "..."}` | Failures after acceptance arrive asynchronously as delivery receipts with a failing `status.state`. See [Delivery Receipts](./loa-webhook.md#delivery-receipts). ### Optional Request Options These properties are available on the LOA send request in addition to the required three. - `scheduled`: An ISO 8601 date-time at which delivery of the message should happen. - `expiry`: An ISO 8601 date-time after which the message should be discarded if it has not been delivered. - `dlrCallbackUrl`: A URI that overrides your account's default delivery receipt callback URL, for this message only. Use it to route delivery receipts for a specific message to a separate handler. - `clientMessageId`: Your own reference, maximum 50 characters. - `channels`: A channel fallback override array. Each entry takes: - `channel` (required): One of `SMS`, `WhatsApp`, `Facebook`, `RCS`, `Viber`, `Line`, `WeChat`, `Zalo`, `Instagram`. Note the capital `L` in `Line` here, which differs from the `line` channel type value used in inbound webhooks. - `fallbackAfter`: Seconds to wait before moving to the next channel. Minimum 10, maximum 86400 (one day). - `successStatus`: One of `Accepted`, `Sent`, `Delivered`, `Read`. This is the status that counts as success on a fallback target, so set it only to a status the target channel actually reports. The `line` channel reports `Accepted` and `Sent`, so set `successStatus` to one of those values when `Line` is a fallback target. > **Note:** The fallback chain configured by 8x8 can use the Accepted or Sent status to evaluate whether to proceed to the next channel. > **Important:** The fallback chain for your account is normally configured by the 8x8 team, including which channels are used, in what order, and the delay between them. Use `channels` only to override that configuration for a specific message. > **Note** > > The `/messages` endpoint is shared across every Messaging Apps channel, so its schema carries `content.interactive`, `content.template`, `content.richCard`, and `content.carousel`. **None of these are supported on the `line` channel.** Do not treat the shared schema as the LINE surface. The supported set is listed in [Supported Types at a Glance](./loa-messaging.md#supported-types-at-a-glance). ## Sending on LINE Official Notification (LON) LON is a separate product, not a message type on the `line` channel. It is strictly one-way, every message populates a template that LINE has pre-approved, and the recipient is a phone number rather than a LINE user ID. It also has its own sub-account and its own bearer token. **Endpoint:** ```json POST https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/lon ``` **Path Parameters:** - `{subAccountId}`: The sub-account provisioned for LINE Official Notification. This is **not** the same sub-account as your LINE Official Account channel. The `user` and `content` properties are required in the request body. **Sample JSON Payload:** ```json { "user": { "msisdn": "+15551234567" }, "clientMessageId": "", "content": { "title": "Event reminder", "company": "8x8", "icon": "calendarCheck", "greeting": "Can't wait to see you there!", "explanation": "Registration opens at 09:00. See you soon." } } ``` **Key Fields:** - `user.msisdn` (required): The recipient's mobile number. International E.164 format with a leading `+` is preferred. - `content.title`: The message title. - `content.company`: The company name shown on the notification. - `content.icon`: The icon displayed on the notification, one of 38 documented values. - `content.greeting`: The greeting line. - `content.explanation`: The explanatory body text. That example is trimmed. The full content schema also carries an emphasised field, a label-and-value list, action buttons, and an `smsFallback` object. For every property, the complete 38-value icon enumeration, the 500-character template limit, and the response shape, see [LINE Official Notification (LON)](./official-notification-lon.md). > **Important:** You cannot compose freeform LON content at send time. Your template must be approved by LINE first, and templates are submitted through 8x8. See [Template Approval](./official-notification-lon.md#template-approval). ## Next Steps **For Developers:** - [LINE Official Account: Behaviour, Messages, and Constraints](./loa-messaging.md) - How the channel addresses a recipient, a complete request body for each of the five supported types, and LINE's real limits - [LINE Official Account Webhooks](./loa-webhook.md) - Inbound messages, retry behaviour, and synchronous errors - [LINE Official Notification (LON)](./official-notification-lon.md) - The full one-way content schema, icon set, and character limits - [LON Delivery Receipts](./lon-webhook.md) - The only LINE delivery receipt on the platform, and what it reports **Important Concepts:** - [Identifying a LINE Recipient](./loa-messaging.md#identifying-a-line-recipient) - Why `channelUserId` and not `msisdn` - [Delivery Receipts](./loa-webhook.md#delivery-receipts) - What statuses the `line` channel reports and what it does not **API Reference:** - [Send Message](/connect/reference/send-message) - The generated reference for `POST /api/v1/subaccounts/{subAccountId}/messages` - [Send LON Message](/connect/reference/send-lon-message) - The generated reference for `POST /api/v1/subaccounts/{subAccountId}/lon` - [Getting started with Messaging API](/connect/docs/messaging-apps-api-get-started) - Cross-channel authentication and server regions --- ## LINE Governance, Security and Compliance LINE's Official Account Guidelines are unusually prescriptive, and enforcement is unilateral: LINE may refuse, suspend, or terminate an account and is under no obligation to explain why, offer compensation, or issue a refund. A team that reads only the API pages will not know this. This page states the rules that apply to your account, the enforcement model behind them, LINE's development obligations that survive the 8x8 abstraction, and the 8x8 platform controls available to you. Sections 1 through 5 are LINE's rules, not 8x8's. The authoritative text is LINE's [Official Account Guidelines](https://terms2.line.me/official_account_guideline_th?lang=en). ## Account Eligibility LINE states that the service is **unavailable** to any company, organization, or individual it deems to fall into any of these categories: 1. Providing a product or service which may be used for the purposes of criminal activity. 2. Engaged in illegal or criminal activity, or facilitating it. 3. Involved in the illegal or illicit sale or purchase of another individual's personal details, registration details, or usage history, or acting as an intermediary or agent for it. 4. Committing acts in violation of laws and ordinances, or of standards of public order and decency, or which may be considered to be in violation of the same. 5. Determined by LINE to have committed any prohibited act established in Article 17 of the Terms of Use. 6. Determined by LINE to be inappropriate as a customer of the service for any other reason. LINE gives examples: parties which may confer any disadvantage to LINE users, may adversely affect trust in LINE's public image, or may involve LINE in complaints, claims for damages, or other conflicts. LINE separately publishes a list of product and service categories for which promotion via LINE is prohibited, and may reject, suspend, or nullify a contract on those grounds. The categories include adult products and services, dating and matchmaking services, get-rich-quick and easy-success marketing schemes, trading of virtual goods or game currency for real money, sale of social media accounts or followers, network marketing schemes, sales using fear or pressure tactics, sales claiming guaranteed outcomes, credit-card-limit cash conversion services, casinos and online casinos, sale or brokerage of personally identifying information, sale of counterfeit or pirated goods, and medical products not yet approved for use in Thailand. > **Important:** LINE states explicitly that this list is **not exhaustive**, and that the service may also be declined or suspended in cases other than those listed. Do not read the published list as a safe harbour. If your business sits near any of these categories, resolve it before provisioning rather than after. ## Prohibited Content and Activities Irrespective of account type, it is your responsibility to ensure that nothing distributed from your account, including chats, posts, and auto-replies, and nothing in your account settings, involves any of the activities prohibited under Article 17 of LINE's Terms of Use. Condensed into the categories that most affect a messaging programme: 1. **Illegality:** - Activities that violate the law, court verdicts, resolutions, orders, or legally binding administrative measures. - Illegal activities, or activities that aid or encourage illegal activity. 2. **Third-party rights:** - Activities that infringe intellectual property rights, including copyright, trademark, patent, fame, and privacy, whether LINE's or a third party's. - Activities that illegally or improperly lead to the collection, disclosure, or provision of a third party's personal information, registered information, or user history. 3. **Third-party advertising and user profiling:** - **Using the account as an advertising medium for a third party is prohibited.** - When delivering advertisements or messages that use deemed attributes, **identifying the attributes of the users who come into contact with them is prohibited.** LINE names two specific techniques: specifying individual transition destinations per attribute, and adding information to the destination URL that makes the transition path traceable. If your campaign design relies on per-segment landing pages or per-segment tracking parameters, it breaches this rule. 4. **Misrepresentation and scope:** - Activities that lead to misrepresentation of LINE or a third party, or that intentionally spread false information. - **Distributing content unrelated to the business you declared at the time of application.** Your account must stay within the business it was approved for. 5. **Harmful expression:** - Violent or sexual expressions; expressions that discriminate by race, national origin, religion, gender, social status, or family origin; expressions that induce or encourage suicide, self-injury, or drug abuse; and anti-social expressions. - Using the service for sexual or obscene purposes, for arranging sexual or romantic encounters, for harassment or libel against other users, or for purposes other than the service's main purpose. 6. **Platform abuse:** - Distributing content that may cause discomfort or inconvenience to users or third parties. - Interfering with the service's servers or network systems, with LINE's operation of the service, or with users' use of it. - Deliberately taking advantage of defects in the service. - Making unreasonable inquiries or undue claims of LINE. 7. **Other LINE terms:** - Distributing content that contravenes the LY Corporation Common Terms of Use or the LINE Logo Usage Guidelines. - Aiding or encouraging any of the above, or anything else LINE deems inappropriate. ### Link and content precautions LINE publishes user-protection precautions that apply to everything distributed from an account, whether or not it is used commercially: - **No links that do not work on a smartphone.** LINE asks you to refrain from posting links that cannot be viewed or operated on iOS or Android, and recommends that every linked page display properly on a smartphone. - **Links must be related to the message.** Links to pages with no direct relationship to the text or creative content are prohibited, as are links to pages not under your practical control. The message title, the content, and the linked page must all be related and must not strike users as unnatural. - **No confusing or non-functional interface elements.** Unclear links, non-functional buttons or menus, and anything else that may confuse users or cause operational errors is prohibited. - **App download prompts.** Consult the App Store and Google Play regulations, alongside LINE's own terms, before inducing users to download an app. LINE also warns that if Apple or Google determines that significant ranking changes resulted from app notifications via LINE, the promoted app risks rejection for ranking manipulation. - **Corrections must be labelled as corrections,** and LINE charges a correction message at the same rate as a normal message. - **Icon image changes are rate-limited by LINE.** An icon image cannot be changed again for one hour after being changed. For accounts whose friend count exceeds a threshold, the icon cannot be changed unilaterally at all and a separate request must be filed. LINE asks you to avoid sudden or frequent changes. ## Advertising and Promotion Rules - **The advertiser's name must be clearly displayed.** In images it must be displayed at a clearly visible size. - **Credit the rights holder.** When you use materials that do not belong to you, the rights holder's name must be displayed and the relationship between the rights holder and you clearly explained. - **Resale of advertising space to a third party is prohibited,** except where LINE specifically permits it. Advertisements for multiple entities, and purchase of advertising space by groups of entities, are not allowed. Using the account to release information about unrelated third parties is fundamentally prohibited on user-protection grounds. - **The three published exceptions** to the resale prohibition are: a clear controlling relationship between the two entities where users will not be confused or misled; a clear logical necessity for the two entities to produce a combined advertisement; or the two entities sharing a **Collaborative Account**, which LINE provides as a separate advertising menu. - **You may promote only your own products and services,** or those produced by an entity with whom you share a Collaborative Account. ## Verified Account Naming Rules LINE requires Verified Account names to follow four rules: 1. Names **must** include the official name of the company, organization, or self-employed person, or the official name of the product or service they provide. 2. Names **must not** imply the existence of companies, organizations, self-employed persons, products, or services which do not exist. 3. Names **must not** include text strings which are non-factual or which may lead to erroneous inferences. 4. Names **must not** imply that they pertain to products or services offered by LINE or its affiliated companies. Names that infringe these rules may be subject to a request for amendment from LINE. LINE states that, in principle, it does **not** accept account name changes for Verified Accounts. Where the company or service name has genuinely changed, a name change request triggers a **separate screening process**. See [Why Verified Account Matters](./concepts-fundamentals.md#why-verified-account-matters). ## Enforcement and Penalties LINE's enforcement model has four properties that materially affect risk planning: - **No pre-publication approval, but real-time monitoring.** LINE states that as a rule there are no pre-posting checks, but that it may monitor all transmissions in real time. Transmissions found to infringe the precautions may be deleted and the associated account suspended. - **Unilateral penalties.** Where LINE determines that content distributed from an account, or the account's settings, are inappropriate or breach the guidelines, LINE may impose any or all of the published penalties, or others of a similar type, including rejecting the opening of accounts, suspending accounts, and nullifying the contract. - **No obligation to explain.** LINE states it **shall not be required in any instance to provide reasons** for any penalty imposed. - **No liability, compensation, or refund.** LINE states it **shall not be subject to any liability, compensation, or refund** arising from a penalty. Two further operational facts belong here: - **A sent message cannot be recalled.** Once transmitted to users, messages **cannot be deleted or amended** by you or by LINE. There is no unsend, and no correction mechanism other than sending a further message clearly labelled as a correction. - **Guidelines and functionality can change without notice.** LINE states that the service's functionality, including the Official Account admin screen, and the content of the guidelines may be changed without prior notice, and that continued use after such a change constitutes your agreement to it. ## Development Obligations These are LINE's own engineering rules for the Messaging API. They apply directly if you operate your own LINE bot server, and the principles behind them survive the 8x8 abstraction. ### Prohibited by LINE - **Do not exceed LINE's rate limits.** Requests over the limit receive `429 Too Many Requests`. Limits are applied **per endpoint, per channel**, and an endpoint with a different HTTP method counts as a different endpoint. Most endpoints allow **2,000 requests per second**, but several are far lower, including 60 requests per hour for broadcast and narrowcast sends and for the statistics endpoints. - **Do not load test through the LINE Platform.** LINE offers no load-testing service and asks you to prepare a separate environment for load testing your own servers. - **Do not send mass messages to the same user.** - **Do not send requests to user IDs that do not exist.** - **Do not attempt to identify user attributes** for a specific user ID, including by using the audience management API or narrowcast messages to infer them. - **Do not restrict access by IP address** on servers that receive LINE webhooks. LINE does not disclose its platform IP addresses and they change without notice. Verify the signature instead. ### Recommended by LINE - **Honour unsend events.** When a user unsends a message, LINE sends an unsend event. LINE asks you to respect the user's intent and handle the message so it cannot be seen or used in future. - **Verify webhook signatures** before processing any webhook event. See [If You Also Receive Webhooks Directly from LINE](./loa-webhook.md#if-you-also-receive-webhooks-directly-from-line). - **Assume non-breaking additions.** LINE may add endpoints, optional request parameters, response fields, webhook event properties, and new enumerated values without advance notice. Build so that none of those break you. - **Keep your own logs.** LINE states it does **not** provide logs for Messaging API requests or for webhooks it sent, even on request. LINE recommends logging the request ID, timestamp, HTTP method, endpoint, and status code for each API call, and the sender IP, timestamp, method, request path, and returned status code for each webhook received. > **Note** > > The rate limits above are LINE's, and they govern calls made directly to the LINE Platform. They are not the throughput of the 8x8 send endpoint. No LINE-specific send throughput limit is published for the 8x8 Messaging Apps API. For account-level protections against traffic abuse, see [Recommendations for securing your traffic](/connect/docs/recommendations-for-securing-your-traffic). ## Data Protection - **Profile access requires consent, and without it a user is unaddressable.** If a user has not consented to allow access to their profile information, that information is omitted from webhook events, which means no user ID reaches you at all. See [Consent and Profile Information](./loa-messaging.md#consent-and-profile-information). - **LINE will not disclose friend or chat information.** LINE states that user information about the account's friends, including account name and LINE ID, and message information sent in chats by those friends, **cannot be disclosed by LINE under any circumstances**, with the exception of certain functions. - **Operator country must be registered and disclosed.** Following amendments to Japan's Act on the Protection of Personal Information that took effect on April 1, 2022, LINE requires the country or region of the account operator to be registered in LINE and disclosed on the account. For a corporation or sole proprietor, that is where the entity is located. For an individual, it is where the individual lives. Register it in LINE Official Account Manager under account details. - **Platform limits on non-smartphone devices.** LINE states that it is a service developed for smartphones, and that while it has minimum functionality for PCs and for smartphones on other operating systems, it cannot guarantee correct display of all Official Account functionality on such devices. ## Regional Availability LINE restricts several parts of the product by country or region. All four of these are LINE's restrictions. | Restriction | Scope | |---|---| | **LINE Official Account is not available in the European Union** | The product itself | | **Verified Account review applications are accepted only for Japan, Taiwan, and Thailand** | Verified Accounts are not being issued for other countries or regions at this time | | **Premium IDs are not available in Indonesia, Singapore, and the United States** | Custom account handles | | **Only the Free plan is available for Indonesia, Singapore, and the United States** | Subscription plans | ## 8x8 Platform Controls These are 8x8's controls, and they apply to the LINE channel the same way they apply to every Messaging Apps channel. - **API key handling.** Your API key is a bearer token and is your master credential for the sub-account. Generate and rotate keys in the [8x8 Connect portal](https://connect.8x8.com/). Never hard-code a key in application source; use environment variables or a secret store. Rotate immediately if you suspect a leak. - **Separate credentials per product.** The LINE Official Account channel and LINE Official Notification are separate sub-accounts with separate keys, which also means a compromised key has a smaller blast radius. See [Sub-accounts and Authentication](./loa-messaging.md#sub-accounts-and-authentication). - **Traffic protection.** See [Recommendations for securing your traffic](/connect/docs/recommendations-for-securing-your-traffic) for the account-level controls available to you. - **Portal access control.** See [Security (SSO)](/connect/docs/connect-security) for SAML-based single sign-on configuration on the 8x8 Connect portal. - **PII removal.** 8x8 provides a [PII Removal API](/connect/reference/delete-pii) for programmatically deleting personally identifiable information from 8x8's logs in line with your own retention policy. - **Data residency.** Choose the platform deployment region appropriate to your data residency obligations. See [Platform Deployment Regions](/connect/docs/platform-deployment-regions) and [Base URLs](./getting-started.md#base-urls). ## References and Resources **LINE's Official Policies:** - [LINE Official Account Guidelines](https://terms2.line.me/official_account_guideline_th?lang=en) - Account types, screening criteria, prohibited activities, advertising rules, and penalties. The authoritative source for sections 1 through 5 of this page - [LINE Official Account Premium ID Terms of Use](https://terms2.line.me/official_account_premiumid_terms_oth) - Premium ID character rules, term, fees, and suspension grounds - [Messaging API development guidelines](https://developers.line.biz/en/docs/messaging-api/development-guidelines/) - LINE's prohibited and recommended engineering practices - [Messaging API rate limits](https://developers.line.biz/en/reference/messaging-api/#rate-limits) - Per-endpoint, per-channel limits - [Verify webhook signature](https://developers.line.biz/en/docs/messaging-api/verify-webhook-signature/) - HMAC-SHA256 signature verification - [Consent on getting user profile information](https://developers.line.biz/en/docs/messaging-api/user-consent/) - When profile information and user IDs are withheld - [LINE Official Account Help Center (Thailand)](https://help2.line.me/official_account_th/web/categoryId/200000074/pc?lang=en) - Regional availability, plans, and account settings **8x8 Documentation:** - [The LINE Ecosystem and Account Model](./concepts-fundamentals.md) - Account types, Verified Account review, and Premium ID - [Account Provisioning and Onboarding](./account-provisioning-onboarding.md) - Provisioning paths, what you submit, and escalation - [LINE Official Account Webhooks](./loa-webhook.md) - The inbound webhook contract and synchronous error handling - [LON Delivery Receipts](./lon-webhook.md) - What the one-way product reports back, and the only LINE delivery receipt on the platform - [Recommendations for securing your traffic](/connect/docs/recommendations-for-securing-your-traffic) - 8x8 account-level traffic protection - [PII Removal API](/connect/reference/delete-pii) - Programmatic deletion of personally identifiable information --- ## The 8x8 LINE Channel Welcome to the 8x8 integration for LINE. This documentation covers both LINE products available on the 8x8 Messaging Apps platform: the two-way **LINE Official Account** channel and the one-way **LINE Official Notification** channel. ## Why Use LINE for Business? LINE is the most popular chat application in **Japan**, **Thailand**, and **Taiwan**. LINE Official Account is LINE's business messaging product. It lets a business hold the same kind of one-to-one conversation with a customer that the customer already has with their friends on LINE, and it is the only route to a LINE user for a business. - **Friend-based reach:** You exchange messages with users who have added your LINE Official Account as a friend, which means your audience has opted in by adding you. - **In-app discoverability:** Accounts that pass LINE's review receive a Verified account badge and appear in LINE in-app search results. Unverified accounts do not appear in search. - **Two-way conversations:** The LINE Official Account channel supports both inbound and outbound messages, so you can run support and service journeys, not just notifications. - **One API across channels:** LINE uses the same 8x8 Messaging Apps send endpoint and webhook envelope formats as SMS, WhatsApp, Viber, and RCS. ## What is the 8x8 Integration for LINE? 8x8 connects your LINE Official Account to the 8x8 Messaging Apps platform, so your application sends and receives LINE messages through the same API and the same webhook you already use for other channels. 8x8 is a LINE-registered Agency (AGP) partner and can provision and manage a LINE Official Account on your behalf. If you already own a LINE Official Account, 8x8 can connect that instead. Both routes are documented in [Account Provisioning and Onboarding](./account-provisioning-onboarding.md). You work with LINE on 8x8 through the **8x8 Messaging Apps API**: send and receive LINE messages programmatically from your own application, CRM, or backend system. ## Two LINE Products on 8x8 LINE appears twice in the 8x8 channel list, as two genuinely different products with different endpoints, different recipient keys, and different capabilities. Choose deliberately. | Feature | LINE Official Account | LINE Official Notification | |---|---|---| | **Channel type value** | `line` | `LineNotification` | | **Direction** | Inbound and outbound | Outbound only | | **Recipient keyed by** | `user.channelUserId` (LINE user ID) | `user.msisdn` (phone number) | | **Send endpoint** | `POST /api/v1/subaccounts/{subAccountId}/messages` | `POST /api/v1/subaccounts/{subAccountId}/lon` | | **Templates** | Not supported | Required, and must be pre-approved by LINE | | **Text limit** | 5,000 characters (LINE limit) | 500 characters per template | | **Content types** | Text, Image, Video, Audio, Location | Text and Button, inside an approved template | | **Delivery statuses reported** | Accepted, Sent | Accepted, Sent, Delivered | The last row is the one most likely to change your design. The `line` channel reports **Accepted** and **Sent** but not Delivered or Read, so you can confirm that a message was accepted by LINE but cannot confirm that the recipient's device received it. The `LineNotification` channel goes one step further and additionally reports **Delivered**. See [LOA Delivery Receipts](./loa-webhook.md#delivery-receipts) for details. > **Key Takeaway:** These are not two modes of one channel. They are two channels, each on its own 8x8 sub-account with its own bearer token, and they identify the recipient in incompatible ways. A phone number cannot be used to send on the `line` channel, and a LINE user ID cannot be used to send on `LineNotification`. ## Where to Start **For All Users (Start Here)** Read the fundamentals first. LINE identifies recipients differently from every phone-number-based channel, and that difference shapes your data model. - **See:** [Identifying a LINE Recipient](./loa-messaging.md#identifying-a-line-recipient) - **See:** [The LINE Ecosystem and Account Model](./concepts-fundamentals.md) - Account types, Verified Account review, Premium ID, and LINE Official Account Manager **For Business and Operations Users** Learn what LINE requires before a single message can be sent, what information you have to supply, and how long it takes. - **See:** [Account Provisioning and Onboarding](./account-provisioning-onboarding.md) - The three provisioning paths, what you submit, and the lead times - **See:** [Governance, Security and Compliance](./governance-security.md) - LINE's account rules and enforcement model - **See:** [Messaging Apps Analytics](/connect/docs/messaging-apps) - Dashboard and reports, filterable by channel **For Developers and Integrators** Start with authentication and the send endpoint, then work through the payload catalogue and the webhook contract. - **See:** [Getting Started with LINE over the 8x8 API](./getting-started.md#authentication) - **See:** [LINE Official Account: Behaviour, Messages, and Constraints](./loa-messaging.md) - How the channel addresses a recipient, plus one complete request body per supported type - **See:** [LINE Official Notification (LON)](./official-notification-lon.md) - The separate one-way product, its full content schema, and its icon set - **See:** [LINE Official Account Webhooks](./loa-webhook.md) - Inbound messages, delivery receipts, and retry behaviour - **See:** [LON Delivery Receipts](./lon-webhook.md) - LON delivery receipts, which additionally report Delivered --- ## LINE Official Account: Behaviour, Messages, and Constraints This is the working reference for two-way conversation messaging on the LINE Official Account channel, `line`. It covers how the channel addresses a recipient, the consent that has to exist before you can address one, how LINE counts what you send, and then one complete request body per supported content type, each followed by its field explanations and its real constraints. The `line` channel supports no templates. If you need a pre-approved structured notification, that is a different product: see [LINE Official Notification (LON)](./official-notification-lon.md). ## LOA Behaviour Three things decide the shape of an LOA integration: the identifier you address a recipient with, the consent that must exist before that identifier ever reaches you, and the sub-account the send is authenticated against. For the objects that sit underneath an Official Account and the channel type value that names each LINE product inside a payload, see [The Technical Building Blocks](./concepts-fundamentals.md#the-technical-building-blocks) and [Channel Type Values](./concepts-fundamentals.md#channel-type-values). ### Identifying a LINE Recipient This is the single biggest difference between LINE and 8x8's phone-number channels. LINE users have no phone number that you can address, so LINE issues an opaque user ID instead. - **`user.channelUserId`** is the field you populate when sending on the LINE Official Account channel. Use it instead of `msisdn` to address the recipient. - **Format:** the LINE Platform issues user IDs as a string matching the regular expression `U[0-9a-f]{32}`. That is the letter `U` followed by exactly 32 lowercase hexadecimal characters. - **Scope:** the user ID is issued per provider. The same person has a different user ID under a different provider, so a user ID you obtained elsewhere is not portable into your LINE integration. - **The same field name carries it back to you.** `user.channelUserId` is also where the LINE user ID arrives on inbound message webhooks, so one field name covers both directions. `channelUserId` also appears in delivery receipts from this channel, which report `Accepted` and `Sent` statuses. See [Delivery Receipts](./loa-webhook.md#delivery-receipts). - **`user.msisdn` is absent for LINE on everything you receive.** The inbound webhook contract states that `msisdn` is left out for channels where users have no phone number, and names LINE as the example. When a `user` field has no value, it is left out of the JSON entirely rather than sent as null. There is also no reason to send `msisdn` on an LOA request: see the [Message API Library](./message-api-library.mdx) for the send payloads. #### How you obtain a channelUserId LINE sends the user ID to the bot server in a webhook when a user adds your LINE Official Account as a friend (a `follow` event) or sends it a message. On 8x8, that arrives at your registered callback URL as an inbound message webhook with the LINE user ID in `user.channelUserId`. LINE also publishes endpoints that return the user IDs of all of your friends, and of all members of a group or multi-person chat. Both are available **only to Verified or Premium accounts**, and neither is exposed through the 8x8 API. > **Key Takeaway:** You cannot send a first message to a LINE user from a phone number. The user must add your LINE Official Account as a friend and produce an inbound event first. Capture and store the `channelUserId` from that event, because it is the only address you will have for that user. ### Consent and Profile Information To access a LINE user's profile information, the user must have consented to allow access to it. Users of LINE for iOS and LINE for Android consent when they begin using LINE. Users who have only ever used LINE for PC cannot consent, and since April 2020 it is no longer possible to create an account on LINE for PC. If a user has not consented, their profile information is omitted from the webhook event, which means **the webhook contains no user ID at all**. Practically, that user is unaddressable: they can add your Official Account as a friend and even send you messages, but you have no identifier to send back to. Other causes of a missing profile, per LINE, are that the user never added your Official Account as a friend, blocked it after adding it, or left the group chat it belongs to. ### Sub-accounts and Authentication The `line` channel is provisioned on its own 8x8 sub-account and authenticated with that sub-account's own bearer token. - **Its own sub-account.** A LINE integration requires a new sub-account. It cannot be an existing SMS sub-account, and it is not the sub-account that serves LINE Official Notification. The `{subAccountId}` in the send URL is the LOA one. - **Its own bearer token.** The API key that authenticates an LOA send does not authenticate a LON send. Generate it in the 8x8 Connect portal, scoped to the LOA sub-account. - **Its own recipient key.** `/messages` on the `line` channel takes `user.channelUserId`. LON's `/lon` endpoint takes `user.msisdn` instead. One LINE Official Account can serve both products at the same time, but each product needs its own 8x8 sub-account. See [The LINE Official Account](./concepts-fundamentals.md#the-line-official-account) for the account-level view, and [LINE Official Notification (LON)](./official-notification-lon.md) for LON's side of the split. > **Important:** Confirm with your account manager which of these sub-accounts you have been provisioned, and which API key belongs to which. Sending a LINE Official Notification payload to `/messages`, or a LINE user ID to `/lon`, will not work. ## Message Counting and Plan Quotas Your LINE Official Account sits on a LINE subscription plan with a monthly allowance of free messages. How LINE counts against that allowance is LINE's rule, not 8x8's, and it surprises people: - **LINE counts by recipient, not by message object.** A single request containing four message objects sent to a chatroom of five people counts as five messages. The number of message objects in a request does not affect the count. - **Messages that cannot be received are not counted.** If you send to a user who blocked your Official Account, or to a user ID that does not exist, that message is not counted. - **Not all sending methods count.** LINE counts push, multicast, broadcast, and narrowcast messages toward the plan allowance. Reply messages are not counted. The 8x8 send request has no reply-token field, so an 8x8 LINE send cannot be a LINE reply message. - **The hard stop is your total monthly ceiling, not the free allowance on its own.** LINE describes this under the heading "When you exceed the limit of free messages", but what it actually enforces is the limit of messages that can be sent in a month: once you exceed it, LINE returns an error response and the message is not sent. Plans that support additional messages let you send past the free allowance and pay per additional message, up to a **maximum number of additional messages** that you configure in LINE Official Account Manager. Your real ceiling on those plans is the free allowance plus that configured maximum. Plans that do not support additional messages have no such extension, so for them the free allowance is the ceiling. > **Note** > > Plan tiers, allowances, and additional-message pricing are LINE's, they are purchased in LINE Official Account Manager, and they vary by country. LINE publishes them itself: see [LINE Official Account (Thailand)](https://lineforbusiness.com/th/service/line-oa-features/broadcast-message). Talk to your 8x8 account manager about sizing the plan for your expected volume. ## Supported Types at a Glance | Content type | Outbound | Inbound | |---|---|---| | **Text** | Yes | Yes | | **Image** | Yes | Yes | | **Video** | Yes | Yes | | **Audio** | Yes | Yes | | **Location** | Yes | Yes | | **File** | **No** | **Yes** | > **Important:** The asymmetry on **File** is real and it catches people out. A LINE user can send your Official Account a file, and you will receive it as an inbound `File` message. There is **no outbound File type** on this channel, so you cannot send one back. For the full inbound contract, including the envelope, the field-by-field description, and the 24-hour expiry on inbound media URLs, see [Inbound Messages](./loa-webhook.md#inbound-messages). For one complete request body per supported type, with screenshots and LINE's real limits, see [Message API Library](./message-api-library.mdx). ## Related Resources **For Developers:** - [Getting Started with LINE over the 8x8 API](./getting-started.md) - Authentication, base URLs, and the optional request properties - [LINE Official Account Webhooks](./loa-webhook.md) - The full inbound contract for all six inbound types, delivery receipts, and retry behaviour - [LINE Official Notification (LON)](./official-notification-lon.md) - Pre-approved structured notifications, the one place LINE templates exist on 8x8 **Important Concepts:** - [The LINE Ecosystem and Account Model](./concepts-fundamentals.md) - Account types, Verified Account review, Premium ID, what a single Official Account can serve, and the Messaging API channel and channel type values underneath it **Cross-channel References:** - [Supported Messaging Apps Content Types](/connect/docs/supported-chat-apps-content-type) - The per-channel content type matrix, including the content types the `line` channel does not support, and character limits - [Supported Messaging Apps](/connect/docs/list-of-supported-chatapps-channels) - Channel type values and supported directions - [Send Message](/connect/reference/send-message) - The generated API reference for the send endpoint **External:** - [LINE Messaging API: Message objects](https://developers.line.biz/en/reference/messaging-api/#message-objects) - LINE's own field-level specification and limits - [LINE Messaging API: Character counting in a text](https://developers.line.biz/en/docs/messaging-api/text-character-count/) - How LINE counts UTF-16 code units and emoji --- ## LINE Official Account Webhooks Everything that arrives at your callback URL for the two-way LINE Official Account channel: inbound messages and delivery receipts. The `line` channel reports **Accepted** and **Sent** delivery statuses but does not report **Delivered** or **Read**. This page documents the inbound contract in full, the delivery receipt contract, and the webhook mechanics both LINE products share. LINE Official Notification goes one step further and additionally reports **Delivered**. For that contract, see [LON Delivery Receipts](./lon-webhook.md). ## Delivery Receipts The LINE Official Account channel reports two delivery statuses: | Status | Reported on `line` | |---|---| | **Accepted** | Yes | | **Sent** | Yes | | **Delivered** | **No** | | **Read** | **No** | After a successful send, delivery receipts arrive at your callback URL as the message progresses through `Accepted` and `Sent`. No receipt arrives for `Delivered` or `Read`, so do not build delivery-confirmation or read-rate reporting that depends on either. LINE Official Notification goes one step further and additionally reports `Delivered`. For that contract, see [LON Delivery Receipts](./lon-webhook.md). Delivery receipts arrive in the **v9** delivery receipt envelope, identical to the one documented in [Delivery receipts for Outbound Messaging Apps](/connect/docs/delivery-receipts-for-outbound-chatapps). **Sample JSON Payload:** ```json { "version": 9, "namespace": "ChatApps", "eventType": "outbound_message_status_changed", "description": "ChatApps outbound message delivery receipt", "payload": { "umid": "", "clientMessageId": "", "subAccountId": "", "channel": "line", "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "status": { "state": "sent", "detail": "delivered_to_operator", "timestamp": "2026-07-29T08:19:47.12Z" } } } ``` **Key Fields:** - `version`: Equals `9` for this format. - `eventType`: `outbound_message_status_changed` for a delivery receipt. - `payload.umid`: The unique message ID returned by the send request. Match it to the `umid` from your send response. - `payload.channel`: `line` for the LINE Official Account channel. - `payload.user.channelUserId`: The LINE user ID of the recipient. - `payload.status.state`: One of `accepted`, `sent`, `rejected`, or `undelivered`. `delivered` and `read` are not reported on this channel. - `payload.status.detail`: Additional context for the status. See [Message status reference](/connect/reference/message-status-references). ## Configuring Your Webhook One callback URL serves **every** Messaging Apps channel on your account. You do not register a separate URL for LINE, and you do not register a separate URL for LON. - Register your callback using the [Webhooks Configuration API](/connect/reference/add-webhooks-1). - Identify the source of each payload from the channel field. On inbound messages that is `payload.recipient.channel`. On delivery receipts it is `payload.channel`. - For the LINE Official Account channel the value is `line`. For LINE Official Notification it is `LineNotification`. - Respond with a `2XX` status. Anything else, including a timeout, triggers the retry sequence described in [Retry Behaviour](#retry-behaviour). One callback URL carries traffic for every channel on your account. You will receive both inbound messages (with `payload.recipient.channel` of `line`) and delivery receipts (with `payload.channel` of `line`) from the LINE Official Account channel, plus delivery receipts from LON (with `payload.channel` of `LineNotification`). If you are on Path B provisioning, remember there is a second, separate webhook configuration: the **Webhook URL** you set in LINE Official Account Manager, under **Settings** > **Messaging API**, which points LINE at 8x8. See [Configuring Your Webhook](./account-provisioning-onboarding.md#configuring-your-webhook). ## Inbound Messages Inbound LINE messages arrive in the **v3** inbound Messaging Apps envelope. Every sample on this page uses v3. Only the LINE Official Account channel has an inbound direction; LON does not. The envelope is identical for all six inbound types. Only `payload.type` and `payload.content` change, so each sample below repeats the same envelope with the content object that type produces. > **Note** > > The currently published usage sample page for LINE, [Line usage samples](/connect/docs/usage-samples-line), shows these inbound webhooks in an **older, differently shaped envelope**: `eventType` of `inboundMessage`, `version` of `1`, flat top-level fields instead of a nested `payload` object, and `recipient.recipientId` instead of `recipient.channel` and `recipient.channelId`. This page uses the current canonical cross-channel v3 envelope documented in [Inbound Messaging Apps message](/connect/docs/inbound-chatapps-message), which every other channel's documentation also uses. **The two published artefacts disagree, and one of them is wrong.** Confirm the shape your account actually receives with a test send before writing your parser against either. ### Text **Sample JSON Payload:** ```json { "version": 3, "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": "", "subAccountId": "", "timestamp": "2026-07-29T05:15:30.00Z", "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "recipient": { "channel": "line", "channelId": "" }, "type": "Text", "content": { "text": "Hello from LINE" } } } ``` **Key Fields:** - `version`: Equals `3` for this format. - `namespace`: Equals `ChatApps` for inbound Messaging Apps messages. - `eventType`: Equals `inbound_message_received`. - `payload.umid`: The unique message ID for the inbound message. - `payload.user.channelUserId`: **The LINE user ID of the sender.** This is the value you store and put back into `user.channelUserId` when you reply. - `payload.user.msisdn`: **Absent for LINE.** The contract states that `msisdn` is left out for channels where users have no phone number, and names LINE as the example. When a `user` field has no value it is omitted from the JSON entirely, not sent as null. - `payload.recipient.channel`: `line` for the LINE Official Account channel. - `payload.recipient.channelId`: The identifier of the channel that received the message. - `payload.type`: The inbound content type. For LINE: `Text`, `Image`, `Video`, `Audio`, `File`, or `Location`. - `payload.content.text`: The text the user sent. - `payload.timestamp`: UTC, ISO 8601. ### Image **Sample JSON Payload:**
View JSON ```json { "version": 3, "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": "", "subAccountId": "", "timestamp": "2026-07-29T05:16:10.00Z", "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "recipient": { "channel": "line", "channelId": "" }, "type": "Image", "content": { "url": "" } } } ```
**Key Fields:** - `payload.type`: `"Image"`. - `payload.content.url`: The pre-signed URL of the image 8x8 has stored on your behalf. See [Inbound media URLs expire](#inbound-media-urls-expire). ### Video **Sample JSON Payload:**
View JSON ```json { "version": 3, "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": "", "subAccountId": "", "timestamp": "2026-07-29T05:16:40.63Z", "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "recipient": { "channel": "line", "channelId": "" }, "type": "Video", "content": { "url": "" } } } ```
**Key Fields:** - `payload.type`: `"Video"`. - `payload.content.url`: The pre-signed URL of the video file. No duration, file size, or thumbnail is included on the inbound side. ### Audio **Sample JSON Payload:**
View JSON ```json { "version": 3, "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": "", "subAccountId": "", "timestamp": "2026-07-29T05:17:10.00Z", "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "recipient": { "channel": "line", "channelId": "" }, "type": "Audio", "content": { "url": "" } } } ```
**Key Fields:** - `payload.type`: `"Audio"`. - `payload.content.url`: The pre-signed URL of the audio file. The `audio.duration` property that an outbound audio send requires has no inbound counterpart. ### File **Sample JSON Payload:**
View JSON ```json { "version": 3, "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": "", "subAccountId": "", "timestamp": "2026-07-29T05:17:30.00Z", "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "recipient": { "channel": "line", "channelId": "" }, "type": "File", "content": { "url": "" } } } ```
**Key Fields:** - `payload.type`: `"File"`. - `payload.content.url`: The pre-signed URL of the file. > **Important:** `File` is **inbound only** on this channel. A LINE user can send your Official Account a file and you will receive it, but there is no outbound `File` type, so you cannot send one back. See [Supported Types at a Glance](./loa-messaging.md#supported-types-at-a-glance). ### Location **Sample JSON Payload:**
View JSON ```json { "version": 3, "namespace": "ChatApps", "eventType": "inbound_message_received", "description": "ChatApps inbound message", "payload": { "umid": "", "subAccountId": "", "timestamp": "2026-07-29T05:17:55.35Z", "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "recipient": { "channel": "line", "channelId": "" }, "type": "Location", "content": { "location": { "longitude": 103.846375, "latitude": 1.289563, "name": "Clarke Quay Riverside", "address": "Clarke Quay, 179019" } } } } ```
**Key Fields:** - `payload.type`: `"Location"`. - `payload.content.location.latitude`: Latitude, as a number. - `payload.content.location.longitude`: Longitude, as a number. - `payload.content.location.name`: The name or title of the location, as the user's LINE client sent it. - `payload.content.location.address`: The street address of the location. ### Inbound media URLs expire > **Important:** Inbound media does not arrive as the sender's original URL. 8x8 hosts the file and gives you a **pre-signed URL that expires after 24 hours**. This applies to `Image`, `Video`, `Audio`, and `File`. Download and persist the file when the webhook arrives if you need it beyond that window, and do not store the URL as though it were permanent. ### No LINE display name in the payload > **Note** > > `payload.user.name` and `payload.user.username` exist in the v3 contract but are WhatsApp profile fields. Do not expect a LINE display name in the inbound payload. `payload.user.channelUserId` is the only identity the inbound webhook carries for a LINE sender. For the complete field-by-field envelope description, including the `interactive` content sub-objects used by other channels, see [Inbound Messaging Apps message](/connect/docs/inbound-chatapps-message). ## Retry Behaviour If 8x8 cannot deliver a webhook to your callback URL, it retries. - **Triggers:** a connection error, a timeout, or an HTTP response code in the `4XX` or `5XX` range. - **Intervals:** progressive retries at **1, 10, 30, and 90 seconds**. What this covers differs by product, because the two products produce different webhooks: - **On the LINE Official Account channel, retry behaviour applies to both inbound message webhooks and delivery receipts.** - **On LINE Official Notification, it only ever concerns delivery receipts.** LON has no inbound direction. Design your handler to be idempotent on `umid`, because a retry can result in the same message being delivered to you more than once. ## Per-Message Callback Override Both LINE endpoints accept a `dlrCallbackUrl` property on the request body. It overrides your account's default delivery receipt callback URL for that one message. The value must be a URI. - **On LINE Official Notification:** `dlrCallbackUrl` on `POST /api/v1/subaccounts/{subAccountId}/lon`. Use it to route receipts for a particular notification campaign or test run to a separate handler. See [Per-Message Callback Override on /lon](./lon-webhook.md#per-message-callback-override-on-lon). - **On the LINE Official Account channel:** `dlrCallbackUrl` on `POST /api/v1/subaccounts/{subAccountId}/messages`. Use it to route delivery receipts for a specific LINE message to a separate handler, for example for campaign-level tracking. ## Errors On the LINE Official Account channel, failures reach you in two ways: synchronously in the HTTP response to your send request, and asynchronously through delivery receipts. ### Synchronous failures These come back in the HTTP response to your send request, before the message enters the platform. They are identical for `/messages` and `/lon`. | HTTP status | Meaning | |---|---| | `200` | The message was accepted. The body carries `umid` and `status.state` of `queued` | | `400` | Bad request. A parameter is missing or invalid | | `401` | The request was not authenticated | | `500` | Internal server error | The error body shape is consistent across all three failure codes: ```json { "code": 1002, "message": "Invalid MSISDN format (not E.164 international number)", "errorId": "", "timestamp": "2026-07-29T08:19:45.99Z" } ``` For the codes that appear in the `code` property of an API error body, see the [API Error Codes](/connect/reference/api-error-codes) reference. ### Asynchronous failures Failures that occur after a message is accepted arrive as delivery receipts with a `status.state` of `rejected` or `undelivered`. These receipts reach your callback URL in the same v9 envelope described in [Delivery Receipts](#delivery-receipts), with the `status.detail` field carrying additional context. For the codes that can appear, see the [General Error Codes](/connect/reference/message-status-references). Because the `line` channel does not report `Delivered` or `Read`, you can confirm that a message was sent to the operator but not that the end user received or read it. Build delivery-confirmation and read-rate reporting on channels that support those statuses. LINE Official Notification additionally reports `Delivered`, giving more visibility into the delivery path. See [Asynchronous Failures](./lon-webhook.md#asynchronous-failures). ## If You Also Receive Webhooks Directly from LINE This section applies **only** if you run your own LINE bot server alongside 8x8 and receive webhooks from the LINE Platform directly. It does not describe the 8x8 webhook covered above. If 8x8 is your only integration, skip it. Only the LINE Official Account channel has a bot-server relationship with LINE at all, so nothing here applies to LON. LINE signs the webhooks it sends to a bot server, and expects the bot server to verify that signature: - **Signature algorithm:** LINE generates a signature with **HMAC-SHA256**, using the webhook event body as the input data and the **channel secret** as the hash key. - **Header:** The signature is sent in the `x-line-signature` request header. - **Do not modify the body before verifying.** The signature is computed over the request body as sent. - **Where the channel secret comes from:** the channel's **Basic settings** tab in the [LINE Developers Console](https://developers.line.biz/console/). Admin privileges on the channel are required to retrieve or reissue it. Reissuing a channel secret immediately invalidates the current one, so assess the impact on anything already using it first. - **Do not allowlist by IP address.** LINE states that it does not disclose the IP addresses of the LINE Platform, and that they are subject to change without notice. Use signature validation instead. - **Keep your own logs.** LINE states that it does not provide logs for Messaging API requests or for webhooks it sent, even on request. You are responsible for saving them. For LINE's full description, see [Verify webhook signature](https://developers.line.biz/en/docs/messaging-api/verify-webhook-signature/) and [Messaging API development guidelines](https://developers.line.biz/en/docs/messaging-api/development-guidelines/). ## Related Resources **For Developers:** - [Getting Started with LINE over the 8x8 API](./getting-started.md) - Authentication, base URLs, and the send response - [LINE Official Account: Behaviour, Messages, and Constraints](./loa-messaging.md) - Outbound payloads, addressing, consent, and message counting - [LON Delivery Receipts](./lon-webhook.md) - LON's extended delivery receipt coverage, including the Delivered status that the LINE Official Account channel does not report - [LINE Official Notification (LON)](./official-notification-lon.md) - The one-way product's request contract **Important Concepts:** - [Identifying a LINE Recipient](./loa-messaging.md#identifying-a-line-recipient) - Where `channelUserId` fits in your data model **Cross-channel References:** - [Inbound Messaging Apps message](/connect/docs/inbound-chatapps-message) - The canonical v3 inbound envelope - [Webhooks Configuration API](/connect/reference/add-webhooks-1) - Registering your callback URL - [API Error Codes](/connect/reference/api-error-codes) - The codes returned in a synchronous error body --- ## LINE Official Notification Delivery Receipts LINE Official Notification is outbound only, so the only webhook it produces is a delivery receipt. It is also the **only LINE product on 8x8 that produces a delivery receipt at all**: the two-way LINE Official Account channel has none. This page is therefore the single home for LINE delivery receipt content, covering the v9 envelope, the full status enumerations, asynchronous failures, and the one request property specific to the `/lon` endpoint. The mechanics that genuinely are shared between the two LINE products are documented once, on the [LOA webhook page](./loa-webhook.md), and linked from here rather than repeated. ## Delivery Receipts for LON LON delivery receipts reach `Delivered`. They do not report `Read`. | Status | `LineNotification` (LON) | |---|---| | **Accepted** | Reported | | **Sent** | Reported | | **Delivered** | **Reported** | | **Read** | **Not reported** | The LINE Official Account channel (`line`) reports `Accepted` and `Sent` but not `Delivered` or `Read`. LON goes one step further, additionally reporting `Delivered`. See [Delivery Receipts](./loa-webhook.md#delivery-receipts) for the LOA delivery receipt contract. > **Important:** Read receipts are not available on either LINE product. Do not build reporting that promises a read rate for LINE. For the `line` channel, do not build delivery reporting at all, because nothing is reported. ### The v9 payload LON receipts arrive in the same **v9** delivery receipt envelope as every other Messaging Apps channel that produces one. **Sample JSON Payload:** ```json { "version": 9, "namespace": "ChatApps", "eventType": "outbound_message_status_changed", "description": "ChatApps outbound message delivery receipt", "payload": { "umid": "", "clientMessageId": "", "subAccountId": "", "channel": "LineNotification", "user": { "msisdn": "+15551234567" }, "status": { "state": "delivered", "detail": "delivered_to_recipient", "timestamp": "2026-07-29T08:19:47.12Z" } } } ``` **Key Fields:** - `version`: Equals `9` for this format. - `eventType`: `outbound_message_status_changed` for a delivery receipt. - `payload.umid`: The unique message ID returned by the `/lon` send request. This is how you match a receipt to a notification. - `payload.clientMessageId`: The custom identifier you supplied on the send request. - `payload.channel`: `LineNotification` for LINE Official Notification. - `payload.user.msisdn`: The recipient phone number in E.164 format. LON keys the recipient by phone number, so this field is present. - `payload.user.channelUserId`: **Not sent for LON.** A LON receipt carries `msisdn`, not `channelUserId`. The LINE Official Account channel's own delivery receipts carry `channelUserId` instead. - `payload.status`: The message status object. ### Status enumerations Both enumerations below are platform-wide and apply to every Messaging Apps channel that produces a delivery receipt, LON included. **Possible `status.state` Values:** - `queued`: The request is accepted and queued for processing. - `rejected`: The request has been rejected by 8x8. - `sent`: The message has been sent to the operator and no acknowledgment has been received yet. - `delivered`: The message has been delivered and confirmation was received from the operator. - `undelivered`: A delivery receipt was received indicating the message was not delivered. - `read`: The message was delivered and read. **Possible `status.detail` Values:** - `delivered_to_operator`: Delivered to the operator. Associated with the `delivered` state. - `delivered_to_recipient`: Delivered to the recipient. Associated with the `delivered` state. - `rejected_by_operator`: Rejected by the operator. Associated with the `undelivered` state. - `undelivered_to_recipient`: Delivered but rejected by the target device. Associated with the `undelivered` state. > **Note** > > The v9 payload carries channel-specific extensions for some channels: a `whatsapp` object, and a WhatsApp-only `outboundContent` object that reproduces the delivered message. **There is no LINE equivalent of either.** A LON delivery receipt carries no LINE-side error code, pricing category, or billable flag, and no copy of the notification content. For the complete envelope description, the other channels' sub-objects, and the older v8 format, see [Delivery receipts for Outbound Messaging Apps](/connect/docs/delivery-receipts-for-outbound-chatapps) and [Message status reference](/connect/reference/message-status-references). ## Asynchronous Failures A failure that happens after the platform has accepted your `/lon` request arrives later, as a delivery receipt with a failing `status.state`. The `status` object carries two extra properties when the state is a failure: - `status.errorCode`: An integer error code, set only for errors. - `status.errorMessage`: A description of the error, set only for errors. **LON delivery receipts carry richer failure detail than LOA's.** The LINE Official Account channel reports `Accepted` and `Sent` but does not report `Delivered`, so its asynchronous failure surface is narrower. See [Asynchronous failures](./loa-webhook.md#asynchronous-failures). > 📘 **Error Code Reference** > > For the delivery receipt error codes, see the [Messaging Apps Delivery Error Codes](/connect/docs/delivery-error-codes#general-error-codes) reference. The **General Error Codes** section applies to LINE. Note that the reference currently has no LINE-specific section: only General, WhatsApp, and Viber sections exist. Several General error codes are directly relevant, including `15` (InvalidDestination: the destination is not valid for that channel or is part of a blacklist on Connect), `2` (ContentRelatedError: the content type is not supported by this channel), `36` (Expired: the message was not delivered at the requested time), and `46` (SubscriberNotReachable: the message was sent to the channel, but the user is not reachable for delivery). ## Shared Webhook Mechanics These are identical for both LINE products and are documented once, on the LOA webhook page: - [Configuring Your Webhook](./loa-webhook.md#configuring-your-webhook) - One callback URL serves every Messaging Apps channel on your account, including LON. You do not register a separate URL for LON, and you identify a LON receipt from `payload.channel`. - [Retry Behaviour](./loa-webhook.md#retry-behaviour) - Retries on a connection error, a timeout, or a `4XX` or `5XX` response, at 1, 10, 30, and 90 seconds. On LON these apply to delivery receipts, since LON produces no other webhook. - [Synchronous failures](./loa-webhook.md#synchronous-failures) - The `400`, `401`, and `500` responses returned on the send request itself, which are the same for `/lon` as for `/messages`. Error handling is partly shared. The synchronous half above is common to both endpoints. Both products produce asynchronous failures via delivery receipts, but LON's receipts reach `Delivered` and carry richer error detail. LOA's asynchronous failures are documented in [Asynchronous failures](./loa-webhook.md#asynchronous-failures), and LON's are documented on this page in [Asynchronous Failures](#asynchronous-failures). ## Per-Message Callback Override on /lon The `/lon` request body accepts a `dlrCallbackUrl` property, the same way the LOA `/messages` body does. It overrides your account's default delivery receipt callback URL for that one notification. - `dlrCallbackUrl` (optional): A URI. Applies to this message only, and does not change your account configuration. Use it to route receipts for a particular notification campaign or test run to a separate handler. For the property in the context of the full LON request body, see [Request Body](./official-notification-lon.md#request-body). > **Note** > > On the `line` channel the same property routes delivery receipts for that message to a separate handler, the same way it does on LON. See [Per-Message Callback Override](./loa-webhook.md#per-message-callback-override). ## Related Resources **For Developers:** - [LINE Official Notification (LON)](./official-notification-lon.md) - The endpoint, the full content schema, the icon set, and the character limits - [LINE Official Account Webhooks](./loa-webhook.md) - The inbound message contract, delivery receipts, retry behaviour, and synchronous errors - [Getting Started with LINE over the 8x8 API](./getting-started.md#sending-on-line-official-notification-lon) - Authentication, base URLs, and a first LON send **Cross-channel References:** - [Delivery receipts for Outbound Messaging Apps](/connect/docs/delivery-receipts-for-outbound-chatapps) - The canonical v9 delivery receipt envelope - [Message status reference](/connect/reference/message-status-references) - The `status` object and its enumerations - [Supported Messaging Apps](/connect/docs/list-of-supported-chatapps-channels) - Channel type values and directions - [Messaging Apps Delivery Error Codes](/connect/docs/delivery-error-codes#general-error-codes) - Delivery receipt error codes --- ## LINE Message API Library import LineTextMsg from './images/line-text-message.png'; import LineImageMsg from './images/line-image-message.png'; import LineVideoMsg from './images/line-video-message.png'; import LineAudioMsg from './images/line-audio-message.png'; import LineLocationMsg from './images/line-location-message.png'; This page provides a complete library of LINE Official Account message API payloads for every supported content type. For channel behaviour, recipient addressing, and message counting, see [LINE Official Account (Two-way)](./loa-messaging.md). > **Note** > > Every 8x8 LINE example writes `type` in **Title Case** (`Text`, `Image`, `Video`, `Audio`, `Location`). The shared `/messages` OpenAPI schema lists the enum in lowercase, but its own inline example uses Title Case. No available source states that the field is case-insensitive, so use Title Case exactly as shown. All character, count, and file-size limits on this page are **LINE's**. No available source states which of them the 8x8 platform pre-validates before forwarding a message to LINE. **Endpoint:** `POST https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/messages` Message Type API Payload ### Text Source: LINE Developers **Send:** `POST https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/messages`
View JSON ```json { "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "type": "Text", "content": { "text": "Hello from 8x8 Messaging API" } } ```
**Key Fields:** - `content.text` (required): The message body. **LINE Limits:** - Maximum **5,000 characters**. - LINE counts in **UTF-16 code units**. Emoji and some Kanji count as more than one character. - LINE emoji placeholders are replaced with alternative text when counting, so a message containing LINE emoji can exceed the limit unexpectedly. ### Image Source: LINE Developers **Send:** `POST https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/messages`
View JSON ```json { "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "type": "Image", "content": { "url": "https://www.example.com/original.png", "image": { "thumbnail": "https://www.example.com/thumbnail.png" } } } ```
**Key Fields:** - `content.url` (required): Public URL of the full-size image. - `content.image.thumbnail`: URL of the thumbnail preview image. **LINE Limits:** | Constraint | Full-size image | Preview image | |---|---|---| | **Format** | JPEG or PNG | JPEG or PNG | | **Max file size** | 10 MB | 1 MB | | **Protocol** | HTTPS (TLS 1.2+) | HTTPS (TLS 1.2+) | | **Max URL length** | 2,000 characters | 2,000 characters | > **Note:** LINE may use the full-size image as the preview instead of the thumbnail you supplied, depending on the device state. ### Video Source: LINE Developers **Send:** `POST https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/messages`
View JSON ```json { "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "type": "Video", "content": { "url": "https://www.example.com/original.mp4", "video": { "thumbnail": "https://www.example.com/preview.jpg" } } } ```
**Key Fields:** - `content.url` (required): Public URL of the video file. - `content.video.thumbnail`: URL of the preview image shown before playback. - `content.video.filesize` (optional): Video file size, in **bytes**. - `content.video.duration` (optional): Duration of the video, in **seconds**. **LINE Limits:** | Constraint | Video file | Preview image | |---|---|---| | **Format** | mp4 | JPEG or PNG | | **Max file size** | 200 MB | 1 MB | | **Protocol** | HTTPS (TLS 1.2+) | HTTPS (TLS 1.2+) | | **Max URL length** | 2,000 characters | 2,000 characters | > **Note:** LINE recommends the video and preview image share the same aspect ratio. A very wide or very tall video may be cropped during playback. ### Audio Source: LINE Developers **Send:** `POST https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/messages`
View JSON ```json { "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "type": "Audio", "content": { "url": "https://www.example.com/original.mp3", "audio": { "duration": 300 } } } ```
**Key Fields:** - `content.url` (required): Public URL of the audio file. - `content.audio.duration` (**required**): Duration of the audio, in **seconds**. > **Important:** The 8x8 field is in **seconds**. LINE's native field is in **milliseconds** (LINE's example: `60000` for one minute). Do not copy a duration value from LINE's documentation into an 8x8 request. **LINE Limits:** - **Format:** mp3 or m4a. - **Max file size:** 200 MB. - **Protocol:** HTTPS (TLS 1.2 or later). - **Max URL length:** 2,000 characters. ### Location Source: LINE Developers **Send:** `POST https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/messages`
View JSON ```json { "user": { "channelUserId": "U3d3edab4f36c6292e6d8a8131f141b8b" }, "type": "Location", "content": { "location": { "latitude": 1.285651, "longitude": 103.847564, "name": "8x8 Office Singapore", "address": "One George Street, Singapore 049145" } } } ```
**Key Fields:** - `content.location.latitude` (required): Latitude, as a number. - `content.location.longitude` (required): Longitude, as a number. - `content.location.name`: Location name (maps to LINE's `title`). - `content.location.address`: Street address. **LINE Limits:** - **Title:** maximum 100 characters. - **Address:** maximum 100 characters. > **Note:** The 8x8 schema marks only `latitude` and `longitude` as required, but LINE marks `title` and `address` as required too. Supply `name` and `address` on every location send. ## Media Hosting Host your media yourself and pass a **publicly reachable HTTPS URL** in `content.url`. Every LINE send example, both in the published usage samples and in 8x8's internal test collection, references an external public URL. The URL must satisfy LINE's requirements: HTTPS with TLS 1.2 or later, percent-encoded using UTF-8, and no longer than 2,000 characters. ## Related Resources **For Developers:** - [LINE Official Account (Two-way)](./loa-messaging.md) - How the channel addresses a recipient, consent, authentication, and message counting - [Getting Started with LINE over the 8x8 API](./getting-started.md) - Authentication, base URLs, and the optional request properties - [LINE Official Account Webhooks](./loa-webhook.md) - Inbound messages, delivery receipts, and retry behaviour **Cross-channel References:** - [Supported Messaging Apps Content Types](/connect/docs/supported-chat-apps-content-type) - The per-channel content type matrix - [Send Message](/connect/reference/send-message) - The generated API reference for the send endpoint **External:** - [LINE Messaging API: Message objects](https://developers.line.biz/en/reference/messaging-api/#message-objects) - LINE's own field-level specification and limits - [LINE Messaging API: Message types](https://developers.line.biz/en/docs/messaging-api/message-types/) - LINE's message type overview with screenshots --- ## LINE Official Notification (LON) LINE Official Notification is a separate product from the LINE Official Account channel, not a message type within it. It has its own endpoint, its own sub-account and bearer token, its own recipient key, its own template approval workflow, and, uniquely among the LINE products on 8x8, a delivery receipt. This page documents the complete request contract, including the full icon set and every content property. For what LON reports back after a send, see [LON Delivery Receipts](./lon-webhook.md). ## How LON Differs from the LINE Official Account Channel | Feature | LINE Official Notification | LINE Official Account | |---|---|---| | **Channel type value** | `LineNotification` | `line` | | **Direction** | Outbound only, strictly one-way | Inbound and outbound | | **Recipient keyed by** | `user.msisdn` (phone number) | `user.channelUserId` (LINE user ID) | | **Endpoint** | `POST /api/v1/subaccounts/{subAccountId}/lon` | `POST /api/v1/subaccounts/{subAccountId}/messages` | | **Templates** | Required, and must be pre-approved by LINE | Not supported | | **Content types** | Text and Button, inside an approved template | Text, Image, Video, Audio, Location | | **Character limit** | 500 characters per template | 5,000 characters per text message | | **SMS fallback** | `smsFallback` object on the request body | `content.sms` on the shared request body | | **Delivery statuses reported** | Accepted, Sent, **Delivered** | Accepted, Sent | | **Sub-account and API token** | Its own | Its own | > **Note** > > LON identifies the recipient by **phone number**, while the LINE Official Account channel identifies them by **LINE user ID**. These are not interchangeable. Whether the LON service resolves a LINE recipient server-side from the phone number, and what happens when no LINE user matches, is not described in any available source. What is documented is the request contract: `/lon` accepts `user.msisdn`. One LINE Official Account can serve both products at once, but each product needs its own 8x8 sub-account, precisely because of this difference in addressing. See [The LINE Official Account](./concepts-fundamentals.md#the-line-official-account). ## Template Approval **LINE Official Notification is strictly one-way, and every template must be approved by LINE before it can be sent.** - You cannot compose freeform LON content at send time. You populate an approved template. - New templates are submitted through 8x8. Contact [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) to submit a template for LINE approval. - Plan template approval into your project schedule the same way you plan account verification. It is a dependency on LINE, not on 8x8. ## Endpoint **Endpoint:** ```json POST https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/lon ``` **Path Parameters:** - `{subAccountId}`: The sub-account provisioned for LINE Official Notification. 3 to 50 characters, restricted to letters, digits, and the characters `-`, `.`, `_`, and `&`. This is **not** the same sub-account as your LINE Official Account channel. Authenticate with `Authorization: Bearer {apiKey}`, using the API key for the LON sub-account. See [Authentication](./getting-started.md#authentication). The `user` and `content` properties are required in the request body. Adjust the base URL for your platform region. See [Base URLs](./getting-started.md#base-urls). ## Request Body The example below populates every available component. Remove the components your approved template does not use. **Sample JSON Payload:** ```json { "user": { "msisdn": "+15551234567" }, "clientMessageId": "", "content": { "title": "Event reminder", "company": "8x8", "icon": "calendarCheck", "greeting": "Can't wait to see you there!", "emphasis": { "label": "Event name", "Content": "8x8 Conference" }, "list": [ { "label": "Date:", "content": "Tue 26/09/2026" }, { "label": "Time:", "content": "09:00 - 16:00" }, { "label": "Venue:", "content": "8x8 Office, 17th Fl." }, { "label": "Seat:", "content": "A-07" } ], "explanation": "We would like to remind you about your reservation for tomorrow's event. Registration opens at 09:00. See you soon.", "actions": [ { "title": "View agenda", "url": "https://www.example.com/agenda" }, { "title": "See directions", "url": "https://www.example.com/directions" } ] }, "smsFallback": { "text": "Event reminder: 8x8 Conference on Tue 26/09/2026", "source": "8x8 events", "encoding": "AUTO" } } ``` ![Sample LON message with all components included](./images/LON%20Event%20Reminder.png) **Key Fields:** - `user.msisdn` (required): The recipient's mobile number. International E.164 format with a leading `+` is preferred. National format is also accepted if you set `user.country`. - `user.country` (optional): A two-character default country code, for example `TH`, used when `msisdn` is in national format. Not needed when `msisdn` is in E.164 format. - `clientMessageId` (optional): Your own unique reference for the message, maximum 50 characters. Echoed back in the response and in delivery receipts. - `dlrCallbackUrl` (optional): A URI that overrides your account's default delivery receipt callback URL, for this message only. - `content.title`: The message title. - `content.company`: The company name shown on the notification. - `content.icon`: The icon displayed on the notification. One of the 38 values listed in [Icon Values](#icon-values). - `content.greeting`: The greeting line. - `content.emphasis.label`: The label of the emphasised field, for example `Event name`. - `content.emphasis.Content`: The value of the emphasised field, for example `8x8 Conference`. - `content.list[].label`: The label of a list row, for example `Date:`. - `content.list[].content`: The value of a list row. - `content.explanation`: The explanatory body text. - `content.actions[].title`: The label on an action button. - `content.actions[].url`: The destination the action button opens. - `smsFallback.text`: The SMS body used if SMS fallback is triggered. - `smsFallback.source`: The SMS sender ID, the "From" field. Maximum 16 characters. - `smsFallback.encoding`: One of `AUTO`, `GSM7`, or `UCS2`. > **Important:** Use capital-C `Content` inside the `emphasis` object, as shown above. This matches the currently published usage sample and 8x8's internal test collection. Be aware that the API schema defines lowercase `content`, but the published samples consistently use `Content`. > **Note** > > Three further schema details worth knowing if you generate a client from the specification. First, `content.actions[].url` is the property name in the schema, and it is what both the published usage sample and 8x8's internal test collection use, but the schema's own inline example writes `content` instead of `url` for the link target. Use `url`. Second, the schema declares `format: uri` on `content.company`, while every example, including the schema's own, puts a plain company name there. Send the company name. Third, `smsFallback.encoding` is defined as an uppercase enum (`AUTO`, `GSM7`, `UCS2`) in the schema, but the published usage sample uses lowercase `auto`. Use the uppercase form shown above, since it matches the schema's enum exactly. The lowercase sample value may not validate. > **Note** > > The `user` object on `/lon` reuses the shared Messaging Apps user schema, so it also lists `channelUserId`, which is the recipient key on the LINE Official Account channel. Only `msisdn`, with optional `country`, is meaningful for LINE Official Notification. ## Icon Values `content.icon` accepts exactly one of the following 38 values. This is the complete enumeration from the API schema. **Possible Icon Values:** - `userPlus` - `chatEllipsis` - `phone` - `note` - `gear` - `bell` - `checkCircle` - `slashCircle` - `search` - `link` - `wallet` - `store` - `mapMarker` - `idCard` - `utensils` - `medicalKit` - `train` - `planeDeparture` - `questionCircle` - `infoCircle` - `boxCheck` - `calendarCheck` - `calendar` - `file` - `envelope` - `usdCircle` - `thbCircle` - `usdCircleSend` - `thbCircleSend` - `invoice` - `couponStar` - `coupon` - `creditCard` - `starCard` - `shoppingBag` - `megaphone` - `shieldCheck` - `history` > **Note** > > No available source publishes a rendered preview of each icon. Pick the value whose name matches your notification's purpose, and confirm the rendering in a test send before going live. ## Character Limits - **500 characters** per LINE Official Notification template. - **16 characters** maximum on `smsFallback.source`. The 500-character limit is a template-level limit, so it constrains the total content you can place across the title, greeting, emphasis, list, and explanation components of one notification. ## Response A successful send returns `200`. **Response:** ```json { "umid": "", "user": { "msisdn": "+15551234567" }, "clientMessageId": "", "status": { "state": "queued", "timestamp": "2026-07-29T08:19:45.99Z" } } ``` **Key Fields:** - `umid`: The unique message ID (a GUID) generated by the 8x8 platform on submission. This is the value that identifies the message in every subsequent delivery receipt. - `status.state`: `queued` on successful submission. This means the request was accepted and queued, not that the message was delivered. - `clientMessageId`: Your own reference, echoed back. - `user`: Echoes the recipient you submitted. The error responses are the same as for the LINE Official Account send endpoint: `400` for a bad request, `401` for a failed authentication, and `500` for an internal error. See [Error responses](./getting-started.md#error-responses). Delivery receipts for LON, which are the only LINE delivery receipts on the platform, are documented in [LON Delivery Receipts](./lon-webhook.md). ## Related Resources **For Developers:** - [LON Delivery Receipts](./lon-webhook.md) - The v9 envelope, the status enumerations, asynchronous failures, and the `dlrCallbackUrl` override on `/lon` - [LINE Official Account Webhooks](./loa-webhook.md) - Callback registration, retry behaviour, and the synchronous error responses shared by both products - [Getting Started with LINE over the 8x8 API](./getting-started.md) - Authentication and base URLs, which apply to LON as well - [LINE Official Account: Behaviour, Messages, and Constraints](./loa-messaging.md) - The two-way channel and its payloads **Important Concepts:** - [Sub-accounts and Authentication](./loa-messaging.md#sub-accounts-and-authentication) - Why LON needs its own sub-account and key - [The LINE Ecosystem and Account Model](./concepts-fundamentals.md#the-line-official-account) - How one Official Account serves both products **Cross-channel References:** - [Supported Messaging Apps](/connect/docs/list-of-supported-chatapps-channels) - Channel type values and supported directions - [Supported Messaging Apps Content Types](/connect/docs/supported-chat-apps-content-type) - The per-channel content type matrix, including LON's 500-character limit - [Send LON Message](/connect/reference/send-lon-message) - The generated API reference for `POST /api/v1/subaccounts/{subAccountId}/lon` --- ## LINE Reference and Resources A lookup companion for the LINE section. LINE and 8x8 use different words for overlapping objects, and the object LINE calls a user ID is the field 8x8 calls `channelUserId`. This page gives you the vocabulary on both sides and the mapping between them. ## Glossary: LINE Terms |Term|Definition| |---|---| |**LINE Official Account (OA)**|The LINE account that represents your business. Users add it as a friend, and messages are exchanged with those friends. On the 8x8 platform this is the `line` channel type value.| |**Unverified Account**|An Official Account that has not been reviewed by LINE. Available to any company, organization, or individual. Does not appear in LINE in-app search results.| |**Verified Account**|An Official Account that has passed LINE's review. Receives a Verified account badge and appears in LINE in-app search results. Review applications are accepted only for Japan, Taiwan, and Thailand, and take about 10 business days.| |**Premium Account**|A tier that LINE may assign against criteria it sets. LINE states it has no responsibility to disclose those criteria. You cannot apply for it the way you apply for a Verified Account.| |**Basic ID**|The account ID that LINE assigns to an Official Account automatically.| |**Premium ID**|A purchased account ID of your own choosing, replacing the basic ID. Up to 18 characters using half-width letters, numbers, dots, hyphens, and underscores. USD 12 per year, auto-renewing annually, non-refundable, and not changeable while in use. On the 8x8 provisioning form this is the **OA ID** field.| |**Business ID**|LINE's common login for its business and developer services, including LINE Official Account Manager and the LINE Developers Console. Up to 100 Official Accounts can be created under one Business ID.| |**Provider**|The LINE entity that owns your channels. Significant because LINE issues a **different** user ID for the same person under a different provider.| |**Channel**|A communication path used to access features of the LINE Platform. Types include the Messaging API channel, the LINE Login channel, and the LINE MINI App channel.| |**Messaging API channel**|The channel created when you enable the Messaging API on an Official Account. This is what 8x8 connects to.| |**Channel ID**|The unique identifier of a Messaging API channel, found in LINE Official Account Manager under Settings > Messaging API (also visible in the LINE Developers Console). Required by 8x8 when you bring your own Official Account.| |**Channel Secret**|A private key known only to LINE and the developer, found on the channel's Basic settings tab. LINE uses it as the hash key when signing webhooks. Admin privileges are required to retrieve or reissue it.| |**Channel access token**|An opaque string proving an application may use a channel. LINE issues four kinds, with lifetimes from 15 minutes to indefinite. 8x8 manages these on your behalf.| |**User ID**|LINE's opaque identifier for a user, format `U[0-9a-f]{32}`. Distinct from a display name and from the LINE ID a user registers to be searchable. On the 8x8 platform it is `channelUserId`, in both directions: you send to `user.channelUserId` and it arrives back in `payload.user.channelUserId`.| |**Friend**|A LINE user who has added your Official Account. Messages are exchanged with friends, and deleting the account deletes all of them.| |**Follow event**|LINE's webhook event fired when a user adds your Official Account as a friend. This is the moment a user ID first becomes available to you.| |**OA Chat package**|A separately purchased subscription add-on for an Official Account, bought in LINE Official Account Manager. If cancelled, it remains usable to the end of the current month, and tags and notes already created are not deleted.| |**LINE Official Account Manager**|LINE's own console for an Official Account. Where you enable the Messaging API and retrieve the Channel ID, Channel Secret, and Webhook URL for it, manage the profile and search visibility, purchase plans and Premium IDs, manage administrators, and view Insights.| |**LINE Developers Console**|LINE's advanced console for channels, also covering other channel types such as LINE Login and LINE MINI App. The Channel ID, Channel Secret, and Webhook URL for a Messaging API channel are visible here too, but LINE Official Account Manager's Messaging API settings page is the primary place 8x8 documentation points you to.| |**Insights**|LINE's own account statistics, inside LINE Official Account Manager. One of only two features that remain usable after an Official Account is deleted, the other being billing.| |**Collaborative Account**|An advertising menu LINE provides separately, which enables the narrow published exceptions to LINE's prohibition on advertising for third parties.| ## Glossary: 8x8 Terms |Term|Definition| |---|---| |**8x8 Account**|Your primary customer account with 8x8.| |**Sub-account**|A logical grouping within your 8x8 Account that owns credentials and channels. All Messaging Apps calls are made in the context of a `subAccountId`. LINE requires a new sub-account, not an existing SMS sub-account, and LINE Official Notification requires its own sub-account separate from the LINE Official Account channel.| |**Channel type value**|The string that identifies a channel in the platform. `line` for the LINE Official Account channel, `LineNotification` for LINE Official Notification, and `Line` in the `channels` fallback override array.| |**API key**|The bearer token that authenticates a Messaging Apps request, generated per sub-account in the 8x8 Connect portal and sent as `Authorization: Bearer {apiKey}`.| |**`umid`**|The unique message ID, a GUID generated by the 8x8 platform when a message is submitted. Returned in the send response, and repeated in every delivery receipt on channels that produce one.| |**`clientMessageId`**|Your own reference for a message, maximum 50 characters. Echoed back in the send response, and in delivery receipts on channels that produce one.| |**`batchId` and `clientBatchId`**|The equivalent identifiers for a batch of messages, 8x8-generated and customer-supplied respectively. Present in delivery receipts for messages sent as a batch.| |**Delivery receipt (DR)**|A `POST` webhook the 8x8 platform sends to your callback URL when a message's status changes. Carries `eventType` of `outbound_message_status_changed`, currently in the v9 format. Both LINE products produce delivery receipts. The `line` channel reports Accepted and Sent. LINE Official Notification additionally reports Delivered.| |**Inbound message webhook**|A `POST` webhook the 8x8 platform sends to your callback URL when a user sends a message. Carries `eventType` of `inbound_message_received`, currently in the v3 format.| |**Callback URL**|The URL on your server where 8x8 delivers webhooks. One URL serves every Messaging Apps channel, and the channel field identifies the source. Registered with the Webhooks Configuration API, and overridable per message with `dlrCallbackUrl`.| |**Channel fallback**|A configured sequence of channels used to reach a recipient, with a wait time between each. Set up by the 8x8 team, and overridable per message with the `channels` array. The `line` channel reports Accepted and Sent, which the fallback chain can use to evaluate whether to proceed.| |**8x8 Connect**|The customer portal, where you generate API keys, manage sub-accounts, and view Messaging Apps analytics.| |**Messaging Apps**|The 8x8 product that provides messaging over LINE, WhatsApp, Viber, RCS, Zalo, and other chat channels.| ## Term Mapping The same object often has one name in LINE's console and another in an 8x8 payload. This table is the bridge. | LINE term | Where it surfaces on 8x8 | |---|---| | **LINE user ID** | `user.channelUserId` on the send request, and `payload.user.channelUserId` on inbound messages. On delivery receipts from the `line` channel it appears as `payload.user.channelUserId`, and a LON receipt carries `msisdn` instead | | **LINE Official Account** | The `line` channel type value, and the channel configured on your sub-account | | **Messaging API channel** | `payload.recipient.channelId` on inbound messages | | **Channel ID and Channel Secret** | Supplied to 8x8 during provisioning. Not exposed in any 8x8 payload | | **Channel access token** | Not exposed. 8x8 authenticates to LINE for you; your application uses an 8x8 API key instead | | **Premium ID** | The **OA ID** field on the 8x8 provisioning information sheet | | **Basic ID** | No 8x8 equivalent. Assigned by LINE and visible in LINE Official Account Manager | | **LINE subscription plan and message allowance** | No 8x8 equivalent field. Managed in LINE Official Account Manager | | **LINE Insights** | No 8x8 equivalent. 8x8 volume reporting is separate, in [Messaging Apps Analytics](/connect/docs/messaging-apps) | | **Follow event** | Surfaces as an inbound message webhook carrying `channelUserId`. LINE's raw event types are not passed through | | **LINE audio `duration` (milliseconds)** | `content.audio.duration` in **seconds**. Do not copy a value across | | **LINE location `title`** | `content.location.name` | ## API Reference Links This section documents guides and payload catalogues. For the complete generated specification, use the API reference. - **Send LINE Official Account message:** `POST /api/v1/subaccounts/{subAccountId}/messages` - See: [Send Your First Message](./getting-started.md#send-your-first-message) - See: [LINE Official Account: Behaviour, Messages, and Constraints](./loa-messaging.md) - See: [Send Message API reference](/connect/reference/send-message) - **Send LINE Official Notification message:** `POST /api/v1/subaccounts/{subAccountId}/lon` - See: [Request Body](./official-notification-lon.md#request-body) - See: [Icon Values](./official-notification-lon.md#icon-values) - See: [Send LON Message API reference](/connect/reference/send-lon-message) - **Register a webhook callback URL:** `POST /api/v1/accounts/{accountId}/webhooks` - See: [Configuring Your Webhook](./loa-webhook.md#configuring-your-webhook) - See: [Inbound Messages](./loa-webhook.md#inbound-messages) - See: [LON Delivery Receipts](./lon-webhook.md) - See: [Webhooks Configuration API reference](/connect/reference/add-webhooks-1) ## Additional Resources **8x8 Cross-channel References:** - [Supported Messaging Apps](/connect/docs/list-of-supported-chatapps-channels) - Channel type values and supported directions - [Supported Messaging Apps Content Types](/connect/docs/supported-chat-apps-content-type) - Per-channel content types and character limits - [Inbound Messaging Apps message](/connect/docs/inbound-chatapps-message) - The canonical v3 inbound envelope - [Delivery receipts for Outbound Messaging Apps](/connect/docs/delivery-receipts-for-outbound-chatapps) - The canonical v9 delivery receipt envelope - [Message status reference](/connect/reference/message-status-references) - The `status` object and its enumerations - [Messaging Apps Delivery Error Codes](/connect/docs/delivery-error-codes#general-error-codes) - Delivery receipt error codes - [Getting started with Messaging API](/connect/docs/messaging-apps-api-get-started) - Cross-channel authentication and server regions **LINE's Own References:** - [LINE Messaging API reference](https://developers.line.biz/en/reference/messaging-api/) - Field-level specification, limits, and rate limits - [LINE Official Account Guidelines](https://terms2.line.me/official_account_guideline_th?lang=en) - Account types, screening, prohibited activities, and penalties - [LINE Official Account Premium ID Terms of Use](https://terms2.line.me/official_account_premiumid_terms_oth) - Premium ID rules and fees - [LINE Official Account Manager](https://manager.line.biz/) - The Official Account console - [LINE Developers Console](https://developers.line.biz/console/) - The channel console --- ## Supported Messaging Apps ### Supported channels and message statuses | Channel | Channel type value | Accepted | Sent | Delivered | Read | |:---------------------------| :----------------- | :------- | :--- |:----------| :--- | | SMS | `sms` | ✅ | ✅ | ✅ | ❌ | | WhatsApp | `whatsapp` | ✅ | ✅ | ✅ | ✅ | | Viber | `viber` | ✅ | ✅ | ✅ | ✅ | | Zalo Notification Service | `ZaloNotification` | ✅ | ✅ | ✅ | ❌ | | LINE Official Notification | `LineNotification` | ✅ | ✅ | ✅ | ❌ | | Line Official Account | `line` | ✅ | ✅ | ❌ | ❌ | | RCS | `RCS` | ✅ | ✅ | ✅ | ✅ | ### Supported channels and directions | Channel | Channel type value | Inbound Message | Outbound Message | |:---------------------------| :----------------- | :-------------- | :--------------- | | SMS | `sms` | ✅ | ✅ | | WhatsApp | `whatsapp` | ✅ | ✅ | | Viber | `viber` | ✅ | ✅ | | Zalo Notification Service | `ZaloNotification` | ❌ | ✅ | | LINE Official Notification | `LineNotification` | ❌ | ✅ | | Line Official Account | `line` | ✅ | ✅ | | RCS | `RCS` | ✅ | ✅ | --- ## Tutorial: Google Sheets and SMS ## Requirements * An 8x8 Connect account * Make.com account * Google Sheet ## Tutorial This tutorial will take you through an example of how to use Make, Google Sheets and 8x8 SMS API together to automate sending an SMS whenever a new entry is added to Google Sheets. ### Google Sheet Setup Before we start on the Make.com scenario, create a new Google Sheet in your Google Account that you can access with the following format. This will be used later to add new rows and trigger the Make.com Scenario. The sheet should have **Phone Number** in Cell A1 and **Message** in Cell B1 to serve as headers. ![Add phone number and message columns](../images/ccee37d-image.png) Add phone number and message columns ### Create a New Scenario The create button is located on the top right from the Dashboard. ![image](../images/7eb5a7f-image.png) ### Add a Trigger The trigger is the first action in a scenario, here we will select **"Google Sheets"** ![image](../images/0b29c8b-image.png) In the list of triggers/actions for Google Sheets, select **"Watch new rows"** ![image](../images/692f4de-image.png) Then you will want to add a new connection. ![image](../images/528ce92-image.png) This will prompt you to connect your Google account and then specify the information about your Google Sheet. ![image](../images/fc6bf87-image.png) It will then prompt you to decide which row to start watching, in this example our Google Sheet is blank so we will select 2 since our new data should start at row 2. ![image](../images/fe57623-image.png) After that, we will select the "Add another module" Option and select 8x8 from the list. ![image](../images/af9ab56-image.png) ![image](../images/cf8f086-image.png) Afterwards, you can specify the following fields which are required: * **Subaccount:** Enter in your 8x8 subaccount * **Destination:** Select the output that contains the Phone Number in the Google Sheet * **Text:** Select the output that contains the Message in the Google Sheet ![image](../images/96ef468-image.png) When you connect for the first time to 8x8, you will be prompted for an 8x8 API Key which can be obtained from the 8x8 Connect Dashboard under the API Keys section. ![image](../images/25f944f-image.png) ### Enable your Make.com Scenario Turn on Scheduling on the Bottom Left and select your desired frequency, by default, it will check every 15 minutes. ![image](../images/e4c1986-image.png) ### Add a Row and Receive an SMS Once you add a row to your Google Sheet, this should trigger Make.com to send you an SMS at the specified phone number. This may not be immediate as it needs to wait for the scheduled interval. ![image](../images/73d092f-image.png) This should result in an SMS being sent to the destination specified. ![image](../images/264e5dd-image.png) ## Potential Future Use Cases Make.com is not limited to using 8x8 with Google Sheets of course. There may be other modules connecting to other apps that would be useful. To see a full list of make.com apps, see their webpage [here](https://www.make.com/en/integrations?addOnApps=1&nativeApps=1). ## Supported Modules Here are the list of supported modules for Make.com: * Send a Single SMS * Send a Bulk SMS * Send a Single Chat App Message * Make an API Call ![image](../images/d29f4aa-image.png) --- ## MakeCall This function should be used to connect the first call with another party. The following is an example of the JSON response you would need to provide: ```json { "clientActionId": "IVRCustomId1", |"callflow": [ { "action": "makeCall", "params": { "source": "6512345678", "destination": "6587654321", } } |] } ``` The action should contain the following parameters: | Name | Type | Description | | ----- | ------ | ------------- | |action | String | makeCall – Action to Connect/Bridge call between two users| |destination | String | Number of the called party in E.164 format (The second user's number).| |source |String |Number of the calling party in E.164 format. This should be the Virtual Number allocated to your sub-account| |clientActionId|String|A custom property that you can use to mark individual actions| --- ## Make.com ## Overview [Make.com](https://www.help.make.com/en/help/apps/communication/8x8) is a platform that allows you to automate workflows across various applications seamlessly. Make enables you to instantly integrate 8x8 with over 1500+ apps, allowing you to automate tasks and unlock productivity enhancements. ### Supported Modules #### Messages * Send a Single SMS * Send Bulk SMS * Send a single Messaging App message (WhatsApp, Viber, etc.) * Watch Outbound Messages Status * Triggers when an outbound message status has been changed * Watch Messaging App Inbound Messages * Triggers when you have a new inbound Messaging App message * Watch SMS Inbound Messages * Triggers when you have a new inbound SMS message #### Other * Make an API Call * Performs an arbitrary authorized API call --- ## Message status reference 8x8 API uses the following universal object for describing the message state across different APIs. Object structure | Parameter name | Type | Description | | --- | --- |------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | state | string | General status of the message.**The property is mandatory for status object and always has a value** | | detail | string | Optional additional detail of the `state` property in status. | | timestamp | string | UTC date and time when the status was observed expressed in [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601): `yyyy-MM-ddTHH:mm:ss.ffZ`**The property is mandatory for status object and always has a value** | | errorCode | integer | Error code for the operation. This property is optional and set only for errors. | | errorMessage | string | Description of the error.This property is optional and set only for errors. | ### State Possible values for `state`: * `queued`: The request is accepted and queued for processing. * `rejected`: The request has been rejected by 8x8 * `sent`: The message has been sent to the operator and we have not received an acknowledgment yet. * `delivered`: The message has been delivered to the destination and we have received confirmation from the operator. * `undelivered`: We have received a delivery receipt from the operator that the message was not delivered. * `read`: The message was delivered and read. ### Detail Possible values for `details`: * `delivered_to_operator`: The message has been delivered to the operator. Associated with `delivered` state * `delivered_to_recipient`: The message has been delivered to the recipient. Associated with `delivered` state. * `rejected_by_operator`: The message has been rejected by the operator. Associated with `undelivered` status. * `undelivered_to_recipient`: The message has been delivered but rejected by the target device. Associated with `undelivered` state. ### Samples of the status object Message sent successfully ```json "status": { "state": "queued", "timestamp": "2020-12-22T01:24:30.6030893Z" } ``` --- ## Getting started with Messaging API Before you get started, please contact your account manager to ensure that your account has access to this product and that the following points have been managed: - You will need a **new sub-account id** to use this API endpoint - it can’t be an existing SMS SubAccount. - In order to use your existing channels such as WhatsApp, Viber, Zalo, Line, etc, we will have to configure them for you. - The fallback mechanism has to be set up by the 8x8 team. You have to define which channels you want to use (and in which order) and the time between each channel is triggered. > 📘 Downloading Messaging APIs (OAS File) > > You can download the OAS File - **[Click Here](https://github.com/8x8Cloud/public-developer-docs/blob/master/docs_oas/connect/business_messaging_api.json)** > > **_ Please do take note that the file provides all the Messaging APIs so please look through the .OAS file and select the specific Messaging API(s) required._** > 🚧 WhatsApp Message Validity Period (TTL) > > The message Time-To-Live (TTL) for WhatsApp Utility/Authentication templates is now customizable. To avoid duplicate messages, ensure your fallback duration exceeds the template's configured TTL. See our [guide](/connect/docs/guide-whatsapp-template-validity-period-ttl) on configuring TTL. Contact your account manager or our [support team](https://connect.8x8.com/support/tickets/create) for further optimization help. Once this is done, you can use Messaging API. The API key is available in the Customer Portal. ### Authentication - 8x8 Messaging API accepts an **ApiKey Bearer Token** authentication method. - You can generate tokens from your customer portal - You need to include the following header in your requests: `Authorization: Bearer {apiKey}` > 📘 > > Replace the `{apiKey}` placeholder with the key generated from the customer portal. > *** If you haven't created your account yet, please go to 8x8 website [https://connect.8x8.com](https://connect.8x8.com) to sign up. ### URL The 8x8 `subAccountId` to use is defined in the URL where you send your request as shown below: `https://chatapps.8x8.com/api/v1/subaccounts/{subAccountId}/messages` > 📘 > > You must replace `{subAccountId}` in the URL above with the sub-account id that you want to use. > ## Server Regions To ensure the use of the correct platform deployment region, it is necessary to modify the base URL to correspond with the provisioned region of your account. Refer to the table below for the appropriate base URL associated with each platform region. For more information on platform regions, please visit the following [page](/connect/docs/platform-deployment-regions#api-endpoints-and-platform-region). **List of server URLs:** | API Region | Base URL | | :------------- | :-------------------------------- | | Asia (default) | | | Europe | | | North America | | | Indonesia | | --- ## Messaging Apps Analytics This section will cover the analytics for Messaging Apps which will feature a dashboard, reports and logs covering all Messaging Apps usage. ## Dashboard The **Dashboard** shows at a glance the current performance of your Messaging Apps. All charts in the Dashboard can be filtered by: * **Subaccount:** Filter by the 8x8 Subaccount used. * **Messaging Apps Channel:** Filter by the Messaging Channel (WhatsApp, Viber, etc.). * **Date Range:** Filter by a date range such as today, last 7 days, last 14 days, last 30 days. ### Message Delivered This chart will show how many messages were delivered during the time range. This features a per day breakdown of the traffic on each day. ![image](../images/ee80ce5-image.png) ### Message Delivered vs Undelivered This chart shows how many messages were delivered versus undelivered on each day. Useful in identifying spikes in traffic that were related to undelivered messages. ![image](../images/ae729bf-image.png) ### Delivery Rate This chart shows how the messages that have a "delivered" status on each day. ![image](../images/0711a1e-image.png) ### Received Messages Shows the incoming messages that were received across all channels. ![image](../images/a3abdc6-image.png) ### WhatsApp Conversations Shows the type of messages that were sent across WhatsApp including: * **Authentication** (2FA OTPs) * **Utility** (Account Updates, Information) * **Service** (User-Initiated) * **Marketing** (Special Offers, Sales, etc.) ![image](../images/4dd0844-image.png) ## Reports The **Reports** page for Messaging Apps will show a further breakdown as compared to the dashboard and allow you to further filter the data. You will also be able to export the information as csv file from the page to import the data into your own analytics systems. ### Reports Chart This chart shows how many messages were sent across each channel, with the additional filters available: **Report Type:** * **WhatsApp Conversations:** Authentication, Utility, Service, Marketing * **By Channel:** Shows the individual channels (WhatsApp, Viber, LINE, etc.) The following sections will show how the report page differs based on the **Report Type**. #### By Channel This report will show traffic by the type of channel (WhatsApp, Viber, LINE, etc.) that is being used. If you are using multiple channels this will allow you to filter out the volume of each channel. ![image](../images/111a5cc-image.png) **Daily Report:** This section shows the type of messages per channel type and date. The available information is: * **Date:** The date the messages were sent/received. * **Total:** Total messages across all channels * **Messaging Apps (Viber, WeChat, Messenger, Kakao Talk, Zalo Notification):** Each column represents the number sent across the respective messaging app. * **Read Rate:** The percentage of messages with a read receipt on each platform * **Delivery Rate:** The percentage of messages with a delivery receipt on each platform. ![image](../images/d243538-image.png) #### WhatsApp Conversations The WhatsApp Conversations Report will show the type of conversations you are having on WhatsApp. ![image](../images/d39b965-image.png) **Daily Report:** This gives insights into the following information for WhatsApp messages. * **Total:** This is the total messages sent. * **Chargeable:** The conversations that are chargeable * **Free:** This applies to the messages WhatsApp gives 1000 free Service conversations each month. They are refreshed at the beginning of each month. Please note that a free tier applies only to Service conversations. * **Customer Initiated:** Messages that are sent from the customer at the beginning of a 24-hour window. * **Business Initiated Messages:** Messages that are sent from the business at the beginning of a 24-hour window. * **Message Category:** As covered earlier, this is the official message categories from WhatsApp * Authentication (2FA OTPs) * Utility (Account Updates, Information) * Service (User-Initiated) * Marketing (Special Offers, Sales, etc.) ![image](../images/2db042d-image.png) ### Export The export feature gives the ability to export the information as a **CSV** file, then send the file as a link to your email. ![image](../images/ea9c1a0-image.png) By default the report is sent to the logged in connect user, however you can specify additional emails. ![image](../images/ed28626-image.png) The resulting file will be a **CSV** file with the following format. ![image](../images/43f8923-image.png) ## Logs The **messaging logs** section will allow you to see information about individual messages. By default this will cover all of the messages across all channels, however you can use the following filters to specify messages. * **Subaccount:** The 8x8 subaccount attached to the messages. * **Channel:** Which Messaging Apps channel (Viber, WhatsApp, LINE, etc.) is used to send/receive the message. * **Direction:** Incoming or Outgoing Message * **Status:** Current Status of the Message * **Delivered:** This means the message has been "delivered to the handset". If the status is not available from the operators, this means that 8x8 has received the confirmation from the carrier that the message has been "delivered to the carrier". * **Read:** This means the message has been read by the recipient (supported by certain Chat Apps channels only) * **Received:** The message has been received by our platform and it is currently being processed before being sent to the carrier. * **Rejected:** The message has not been accepted by our platform. This can be due to some errors such as incorrect mobile numbers or insufficient credit. You will not be charged for rejected messages. * **Sent:** The message has been sent to the operator and 8x8 has not received an acknowledgment yet from the operator. * **Undelivered:** We have received confirmation that the message was not delivered. This can be due to various reasons such as: * Mobile handset is unavailable (e.g. mobile is switched off or on roaming mode) * Filtered out by the operator * **Country:** The destination country of the message. * **UMID:** Filter by a specific Unique Message ID. ![image](../images/2e8906b-image.png) ### Export Similar to the reports section, the messaging logs can be sent to an email address as a **CSV** file. ![image](../images/ea0080d-image.png) The email will contain logs for each of the subAccounts that are associated with this account. ![image](../images/f10bdc1-image.png) Below is an example of a exported for WhatsApp and the information that is available. ![WhatsApp Export Columns 1/2](../images/1f4fcad-image.png) ![WhatsApp Export Columns 2/2](../images/7610a02-image.png) --- ## MoEngage - SMS Integration ## Introduction 8x8 CPaaS (Communications Platform as a Service) offers a robust suite of APIs and tools for integrating voice, video, chat, WhatsApp (among other messaging apps channels) and SMS capabilities into your applications. > 📘 **Prerequisites** > > Ensure that you have your 8x8 account, subaccount and an API key. If there are any issues with your 8x8 credentials reach out to [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) > > ## Video Demo This is an accompanying video guide. It shows how the integration is setup and used in action. ## Configure 8x8 as a Custom SMS Connector (Service Provider) This article helps you configure 8x8 as a Custom SMS Connector (Service Provider) on the MoEngage platform for businesses that use MoEngage for their communication campaign scheduling. ### Requirements Before proceeding, ensure that you have the following parameters to make calls to the 8x8 SMS API. Both parameters below can be found in your 8x8 Connect Portal in the [API Keys section](https://connect.8x8.com/messaging/api-keys). | Parameter | Description | |----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | **API Key** | Used to authenticate with 8x8's APIs. Please see [this](/connect/docs/authentication) page for details. | | **Subaccount** | Your 8x8 subaccount. Your 8x8 account should have a subaccount created for it by default. You can request a new subaccount from our support team at [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com). | ## Configure in Revamped UI Log in to the MoEngage Dashboard and follow these steps: 1. Navigate to **Settings -> Channel -> SMS**. 2. On the **Sender Configuration** tab, click **+ Add sender.** The Add sender page is displayed. 3. In step 1 "Service Provider", click **+ Add custom service provider**. The Add Custom Connector sender page is displayed. 4. Add the following details in step 2 "Sender Details". | Field | Description | | --- | --- | | **Mark as Default** | Turn this toggle on to mark the sender as the default sender for the service provider being configured. If marked as default, this sender will be used for sending all SMS campaigns from MoEngage unless you select a different sender while creating the campaign. | | **Service Provider Name** | This field identifies the service provider you are configuring on the MoEngage Dashboard and has to be unique. Enter **"8x8 SMS”.** | | **Sender Name** | This field identifies the sender. Enter **"8x8 SMS".** | | **Sender Type** | Select the sender type. Available options are:Transactional: Select this option to use the sender for sending alerts about transactions, OTPs, security information, or any information that can be classified as transactional in nature.Promotional: Select this option to use the sender for sending information about your brand, promoting deals, or engaging with users. | 5. Configure the Webhook by adding the following details: | Field | Description | | --- | --- | | **API URL** | This field contains information about the URL that should be used to send an API request to the sender. You can get this information from the API documentation of the sender. Enter the API Endpoint of the sender here. The API URL for 8x8 is: [https://sms.8x8.com/api/v1/subaccounts/{Subaccount}/messages](https://sms.8x8.com/api/v1/subaccounts/%7Bsubaccount%7D/messages)**{subaccount}** in the URL should be replaced by your 8x8 subaccount. This can be found from the 8x8 Connect Dashboard in the [API Keys section](https://connect.8x8.com/messaging/api-keys). | | **Method** | Select **POST** as the HTTP method. | | **URL parameters** | Add the URL Parameters to be passed to the API as Key-Value pairs using this option. You can get this information from the API documentation of the sender. For example, if the API URL call uses the GET method, all the parameters such as API Key, Authorization, and so on, are passed as URL Parameters. | | **Headers** | Add the Request Headers to be passed to the API as Key-Value pairs using this option.**Content-Type:** application/json**Authorization:** Bearer {Api Key}You can find your API key in the 8x8 Connect Dashboard's [API Key section](https://connect.8x8.com/messaging/api-keys). | | **Body type** | Configure the body for your requests. Select JSON and add thefollowing details:In the **message** field, add Moesms_message to pass the MoEngage message into the request.In the **sender** field, add the Sender ID in your 8x8 account.In the **recipient** field, add Moesms_destination to add the recipient to the message. | 6. Click **Send Test SMS** to verify your configuration. 7. Click **Save** to save the sender configuration. 8. You can configure delivery tracking after creating the sender in the MoEngage Dashboard. 9. You can map the attributes of the delivery tracking response manually or automatically. --- ## MoEngage - WhatsApp Integration [MoEngage](https://www.moengage.com/) allows you to launch, measure, and automate campaigns on WhatsApp, harmonizing all of your marketing channels. With the help of MoEngage, you can bring customer data from different systems, devices, and channels into a single profile. Then, deliver timely and relevant campaigns that meet your customers in the right places and right ways along their customer journey. With 8x8 cloud communication platform, businesses and developers alike can incorporate WhatsApp functionality into one of their communications channels. ## Demo Video Please see the companion video guide below to see a demonstration on how to setup MoEngage to send WhatsApp messages. ## Sample use cases * **Promotional Offers**You can send coupons, discounts or sale alerts to customers via WhatsApp using MoEngage's segmentation and messaging tools. * **Order Updates**Keep customers informed by sending WhatsApp notifications when an order is shipped, out for delivery, delivered and other status changes. * **Reminders**Remind customers about upcoming appointments, events, or flight bookings via WhatsApp reminder messages sent through MoEngage. * **Alerts and Notifications**Use MoEngage to send timely WhatsApp alerts about new products, service changes, maintenance notices, and other important notifications. ## Product Scope * MoEngage ## What you'll need * Paid 8x8 Connect account with Whatsapp channel configured * Paid MoEngage subscription ## Setup The setup guide consists of 2 steps: 1. Sender configuration(once-off) 2. Delivery tracking configuration (once-off) 3. Whatsapp template configuration (to be done every time you have a new Whatsapp template) ### Sender Configuration To configure a Sender from 8x8 on the MoEngage Dashboard, go to Settings->WhatsApp->Sender Configuration. Choose 8x8 from the left list menu and click on `+ Sender`, and configure details. ![Whatsapp Sender Configuration on MoEngage](../images/2b3d7b7-image.png)Whatsapp Sender Configuration on MoEngage 1. Sender Name (*Mandatory*): The Sender Name is a name you want to provide to the Sender profile so that you can recognize this easily while using it to create a campaign inside MoEngage. The Sender Name should be between 5-50 characters. 2. WhatsApp business number (*Mandatory*): It is the phone number registered with WhatsApp Business Platform via 8x8, using which you want to send out WhatsApp Messages to your users. On [8x8 Connect](https://connect.8x8.com/chat/channels), you can find your Whatsapp business numbers on the Left Menu -> Chat Apps -> Channels. 3. API URL (*Mandatory*): Log in to the [8x8 Connect](https://connect.8x8.com/messaging/api-keys) portal. From the left menu, go to "API keys" section. Under instructions, click on the subaccount of your choice to copy it. Then, replace "Your_Chosen_Subaccount" with the copied subaccount in the URL below [https://chatapps.8x8.com/api/v1/subaccounts/Your_Chosen_Subaccount/partners/moengage/wa](https://chatapps.8x8.com/api/v1/subaccounts/Your_Chosen_Subaccount/partners/moengage/wa) 4. Authorization (*Mandatory*): From the same "API keys" page, scroll down and you can select an existing API key or create an API key specifically for MoEngage integration. ### Delivery Tracking To track the delivery of your WhatsApp Messages inside MoEngage, you would need to copy MoEngage Delivery Tracking URL and share it with [our support team](mailto:cpaas-support@8x8.com) for us to configure it for you. You should be able to see the URL as shown below: ![image](../images/a9bfdfa-image.png) ### Configuring Whatsapp templates As of October 2023, MoEngage does not retrieve your Whatsapp templates automatically. You will need to set up the approved Whatsapp templates verbatim in MoEngage. 1. Log in to the [8x8 Connect](https://connect.8x8.com/messaging/api-keys) portal. 2. Navigate to Chat Apps -> Whatsapp Templates. 3. Click on the template that you'd like to use on MoEngage to preview the contents of the template. If you don't have any already, you will need to create them. 4. Follow this [MoEngage guide](https://help.moengage.com/hc/en-us/articles/4951072814100-WhatsApp-Templates) on how to copy the Whatsapp template from 8x8 Connect to MoEngage. 5. Repeat steps 3 and 4 for other **approved** Whatsapp templates you'd like to use on MoEngage. --- ## MoEngage [MoEngage](https://www.moengage.com/) has integrations for both 8x8's WhatsApp and SMS channels. Please see the subsections for further details: * [WhatsApp](/connect/docs/moengage-whatsapp) * [SMS](/connect/docs/moengage-sms-integration) --- ## Number Lookup Error Codes The 8x8 Number Lookup API uses the following **error codes:** | HTTP Status Code | Reason | | --- | --- | | 2000 | Internal error / Unknown provider error | | 2004 | Provider Timeout | | 2005 | Provider Error | | 2006 | The pricing plan for that request plan is not configured for particular SubAccount | | 2007 | Billing error. Not enough Account balance | | 6000 | Live lookup on destination operator is currently not available | | 6001 | SIM card is offline | | 6002 | Mobile subscriber not reachable | | 6003 | SIM card is deactivated | | 6004 | Routing error | | 6005 | SIM card is full | --- ## Okta - Bring Your Own Telephony (BYOT) via Voice ## Overview This guide demonstrates how to implement voice-based One-Time Password (OTP) delivery for Okta authentication using 8x8's Voice API. This integration allows you to create a custom telephony provider that delivers OTPs via voice calls instead of SMS. The integration works by creating a webhook service that receives OTP delivery requests from [Okta's Inline Hook](https://developer.okta.com/docs/api/openapi/okta-management/management/tag/InlineHook/#tag/InlineHook/operation/createTelephonyInlineHook) and uses 8x8's Voice API to deliver the OTP via a voice call to the user's phone number. ### Integration Flow **Detailed Flow:** 1. **User initiates login** with multi-factor authentication enabled 2. **Okta triggers** the Inline Hook with user details and OTP 3. **Your webhook service** receives the request and extracts phone number and OTP 4. **Service calls 8x8 Voice API** to initiate a voice call with the OTP message 5. **User receives voice call** with spoken OTP and completes authentication ## Prerequisites Before you begin, ensure you have: - **Okta Account** with admin access to configure Inline Hooks - **8x8 Account** with access to Voice API credentials - **JavaScript/Node.js knowledge** for backend development - **Public HTTPS endpoint** for receiving Okta webhooks (consider using ngrok for development) ## Requirements - Node.js - npm package manager ## Implementation ### Step 1: Backend Code Setup Create a new Node.js project and install dependencies: ```bash mkdir okta-voice-otp cd okta-voice-otp npm init -y npm install express dotenv ``` ### Step 2: Environment Configuration Create a `.env` file with your configuration: ```env # Server Configuration PORT=3000 OKTA_SECRET=your_okta_webhook_secret_here # 8x8 Voice API Configuration EIGHTYEIGHTX_SUBACCOUNT_ID=your_subaccount_id EIGHTYEIGHTX_API_KEY=your_api_key EIGHTYEIGHTX_SOURCE_NUMBER=+1234567890 # Voice Configuration VOICE_PROFILE=en-US-Jenny OTP_REPETITIONS=2 ``` ### Step 3: Main Server Implementation Create `server.js`: ```javascript const express = require('express'); const https = require('https'); const dotenv = require('dotenv'); dotenv.config(); const app = express(); const PORT = process.env.PORT || 3000; // Middleware app.use(express.json()); // Health check endpoint app.get('/health', (req, res) => { res.status(200).json({ status: 'OK', timestamp: new Date().toISOString() }); }); /** * Sends an OTP to a user via an 8x8 voice call. * @param {string} phoneNumber - The recipient's phone number in E.164 format * @param {string} otpCode - The one-time password to be delivered * @returns {Promise} Response from 8x8 API */ async function sendVoiceOTP(phoneNumber, otpCode) { const subaccountId = process.env.EIGHTYEIGHTX_SUBACCOUNT_ID; const apiKey = process.env.EIGHTYEIGHTX_API_KEY; const sourceNumber = process.env.EIGHTYEIGHTX_SOURCE_NUMBER; const voiceProfile = process.env.VOICE_PROFILE || 'en-US-Jenny'; const repetitions = parseInt(process.env.OTP_REPETITIONS) || 2; const payload = JSON.stringify({ callflow: [ { action: 'makeCall', params: { source: sourceNumber, destination: phoneNumber } }, { action: 'say', params: { text: `Your verification code is ${otpCode.split('').join(', ')}. I repeat, your verification code is ${otpCode.split('').join(', ')}.`, voiceProfile: voiceProfile, repetition: repetitions } }, { action: 'hangup' } ] }); const options = { hostname: 'voice.wavecell.com', port: 443, path: `/api/v1/subaccounts/${subaccountId}/callflows`, method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${apiKey}`, 'Content-Length': Buffer.byteLength(payload) } }; return new Promise((resolve, reject) => { const req = https.request(options, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', () => { try { const response = JSON.parse(data); if (res.statusCode >= 200 && res.statusCode < 300) { console.log('[8x8 Service] Voice call initiated successfully:', response); resolve(response); } else { console.error('[8x8 Service] API error:', response); reject(new Error(`8x8 API error: ${response.message || 'Unknown error'}`)); } } catch (error) { console.error('[8x8 Service] Failed to parse response:', error); reject(error); } }); }); req.on('error', (error) => { console.error('[8x8 Service] Request error:', error); reject(error); }); req.write(payload); req.end(); }); } /** * Validates the incoming Okta webhook request * @param {Object} req - Express request object * @returns {boolean} True if request is valid */ function validateOktaRequest(req) { // For production, implement proper Okta webhook signature validation if needed const oktaSecret = process.env.OKTA_SECRET; const authHeader = req.headers.authorization; // If auth header is provided, validate it if (authHeader && oktaSecret) { return authHeader === `Bearer ${oktaSecret}`; } // Allow requests without auth headers for simplicity // In production, consider implementing webhook signature validation return true; } // Okta Voice OTP webhook endpoint app.post('/okta-voice-otp', async (req, res) => { try { console.log('[Server] Received Okta webhook request'); // Validate request if (!validateOktaRequest(req)) { console.warn('[Server] Unauthorized request received'); return res.status(401).json({ error: 'Unauthorized' }); } // Extract data from Okta payload const { data } = req.body; if (!data || !data.userProfile || !data.messageProfile) { console.error('[Server] Invalid Okta payload structure'); return res.status(400).json({ error: 'Invalid payload structure' }); } const phoneNumber = data.messageProfile.phoneNumber || data.userProfile.mobilePhone; const otpCode = data.messageProfile.otpCode; if (!phoneNumber || !otpCode) { console.error('[Server] Missing phone number or OTP code'); return res.status(400).json({ error: 'Missing required fields' }); } console.log(`[Server] Processing OTP delivery for ${phoneNumber.replace(/\d(?=\d{4})/g, '*')}`); // Respond to Okta immediately res.status(200).json({ commands: [{ type: 'com.okta.telephony.action', value: [{ status: 'SUCCESSFUL', provider: '8x8-voice' }] }] }); // Handle voice call asynchronously try { await sendVoiceOTP(phoneNumber, otpCode); console.log('[Server] Voice OTP delivery initiated successfully'); } catch (error) { console.error('[Server] Failed to send voice OTP:', error.message); // Note: We already responded to Okta, so this is logged for monitoring } } catch (error) { console.error('[Server] Webhook processing error:', error); res.status(500).json({ error: 'Internal server error' }); } }); // Global error handler app.use((error, req, res, next) => { console.error('[Server] Unhandled error:', error); res.status(500).json({ error: 'Internal server error' }); }); // Start server app.listen(PORT, () => { console.log(`[Server] Voice OTP service running on port ${PORT}`); console.log(`[Server] Webhook endpoint: http://localhost:${PORT}/okta-voice-otp`); }); module.exports = app; ``` ### Step 4: Package Configuration Update your `package.json`: ```json { "name": "okta-voice-otp", "version": "1.0.0", "description": "Okta BYOT with 8x8 Voice API for OTP delivery", "main": "server.js", "scripts": { "start": "node server.js", "dev": "node server.js" }, "keywords": ["okta", "8x8", "voice", "otp", "byot"], "author": "Your Name", "license": "MIT", "dependencies": { "express": "^4.18.0", "dotenv": "^16.0.0" } } ``` ## Configuration ### Step 5: Okta Inline Hook Setup 1. **Access Okta Admin Console** - Log into your Okta organization as an administrator - Navigate to **Workflow > Inline Hooks** 2. **Create New Inline Hook** - Click **Add Inline Hook** - Select **Telephony** as the hook type ![image](../images/fa1267cdfaf02f591262999bb5179c2038a5097ceefed712857dbd0ecd9099d2-image.png) 3. In the **Create Inline Hook** page, fill in the following values. | Field | Description | Example Value | |-------|-------------|---------------| | **Name** | Can be any value. We use "8x8 - Voice Authentication" | 8x8 - Voice Authentication | | **URL** | Should be set to the URL of your backend server that you send the code. | | | **Authentication Field** | Will be sent as part of the request header. Authentication Field is for your backend to authenticate the webhook from Okta. It can be any valueRefer to Okta's [page](https://developer.okta.com/docs/reference/hooks-best-practices/) on authentication and Inline Hooks for reference. | Authentication | | **Authentication Secret (Used with Authentication Field)** | Will be sent as part of the request header. This should be the value your backend uses to authenticate | secretvalue | After entering the values, click **Save**. 4. **Configure Hook Settings** - Set the hook to trigger on authentication events - Enable the hook for your organization ### Step 6: Authentication Policy Configuration 1. **Create Authentication Policy** - Navigate to **Security > Authentication > Authentication Policies** - Create a new policy or edit existing one 2. **Configure MFA Rules** - Add a rule that requires phone verification - Set the telephony provider to use your custom hook - Configure when voice calls should be used (fallback, primary, etc.) 3. **Assign Policy** - Assign the policy to relevant applications and user groups ## Testing the Integration ### Step 7: Test the Implementation 1. **Start Your Service** ```bash npm start ``` 2. **Expose Your Local Service** (for development) ```bash # Using ngrok ngrok http 3000 ``` 3. **Update Okta Hook URL** with your ngrok URL 4. **Test Authentication Flow** - Attempt to sign in to an application with MFA enabled - Verify that voice call is initiated - Complete authentication with received OTP ## Request/Response Examples ### Okta Webhook Request Example ```json { "eventId": "rZxqX4QGT1KIwr8KSh3C6A", "eventTime": "2025-09-29T09:05:04.000Z", "eventType": "com.okta.telephony.provider", "eventTypeVersion": "1.0", "contentType": "application/json", "cloudEventVersion": "0.1", "source": "https://integrator-xxxxxx.okta.com/api/v1/inlineHooks/calhawlks9zOkRrau0h7", "requestType": "com.okta.user.telephony.mfa-verification", "data": { "context": { "request": { "id": "8d9c47117943da0585412539965xxxx", "method": "POST", "url": { "value": "/api/internal/v1/inlineHooks/com.okta.telephony.provider/generatePreview" }, "ipAddress": "42.61.17.54" } }, "userProfile": { "firstName": "Harris", "lastName": "Doe", "login": "harris@example.com", "userId": "00uvw9bdliXTkH8qR697" }, "messageProfile": { "msgTemplate": "Your code is 11111", "phoneNumber": "9876543210", "otpExpires": "2025-09-29T09:09:59.759Z", "deliveryChannel": "Voice", "otpCode": "11111", "locale": "en" } } } ``` ### Your Service Response Example ```json JSON { "error": null, "commands": [ { "type": "com.okta.telephony.action", "value": [ { "status": "SUCCESSFUL", "provider": "8x8-voice", "transactionId": null, "transactionMetadata": null } ] } ], "debugContext": {} } ``` ## Using the Inline Hook Now that the Inline Hook has been added, in order to require it for signing into your Okta organization. ### Add Authenticator Ensure that in the **Security - Authenticators** page that Phone is added as an Authenticator option. ![image](../images/7a5bf0e-image.png) If it is not already on the list then click **Add Authenticator** to add it. ### Create new Authentication Policy Rule Click **Add Rule** on the **Security - Authentication Policies** page. ![image](../images/c22d8d7-image.png) In the **Edit Rule** page, the only change we will make is for AND Authentication methods where we should include the **Phone - Voice** method along with any other methods we wish to offer the user authenticating into Okta. ![image](../images/93ecf2c74082d634d0b4d85226e447653fb7fdc57e6eefaa8c7206d5970f4c31-image.png) ### Add to Application After creating the Policy, add it to one of your Applications. ![image](../images/37a551d-image.png) ![image](../images/2959b4b-image.png) ### Signing In When attempting to login to the application that you have configured above, you should receive the following screen prompting you to register for Phone Verification. The code should be sent to your phone via a phone call, follow the prompts to finish logging into application. ### Security Considerations - **Webhook Validation**: Implement proper signature validation for production use - **Environment Variables**: Never hardcode credentials in your source code - **HTTPS**: Always use HTTPS for webhook endpoints in production - **Rate Limiting**: Implement rate limiting to prevent abuse - **Logging**: Log events for monitoring but avoid logging sensitive data ### Troubleshooting #### Common Issues 1. **Voice call not initiated** - Verify 8x8 API credentials and subaccount ID - Check phone number format (E.164) - Review API response for error messages 2. **Okta webhook not received** - Confirm webhook URL is accessible from internet - Verify SSL certificate is valid - Check Okta hook configuration and authentication 3. **Authentication failures** - Validate webhook secret configuration - Review request headers and authentication method #### Monitoring and Logs Monitor your service logs for: - Incoming Okta webhook requests - 8x8 API responses - Error conditions and failed calls - Performance metrics ## Conclusion You've successfully implemented voice-based OTP delivery for Okta using 8x8's Voice API. This integration provides an alternative to SMS for users who prefer or require voice-based authentication methods. --- ## Okta - Bring Your Own Telephony (BYOT) [Okta BYOT](https://support.okta.com/help/s/article/bring-your-own-telephony-required-for-sms-and-voice) enables organizations to use 8x8 for Multi-Factor Authentication (MFA) to provide secure user access, benefiting companies that need to meet regulatory compliance or prefer to leverage existing telephony investments for MFA. Choose your OTP delivery method for Okta multi-factor authentication: ## SMS Delivery **[SMS BYOT Guide](/connect/docs/okta)** Text message delivery using 8x8 SMS API ## Voice Delivery **[Voice BYOT Guide](/connect/docs/okta-bring-your-own-telephony-byot-via-voice-otp)** Automated voice call delivery using 8x8 Voice API --- ## Okta - Bring Your Own Telephony (BYOT) via SMS ## Overview This guide will take you through how to integrate 8x8's SMS API into Okta as an authenticator method using an Okta [Inline Hook](https://developer.okta.com/docs/concepts/inline-hooks/). Specifically we will be using Okta's [Telephony Inline Hook](https://developer.okta.com/docs/reference/telephony-hook/) to add 8x8's SMS API as an option. For Okta's own guide on bringing your own telephony provider, refer to Okta's reference guide [here](https://support.okta.com/help/s/article/bring-your-own-telephony-required-for-sms-and-voice?language=en_US) and use Option 2. The diagram below explains how the flow will look like from using Okta together with the Node.js server we will be building in this tutorial to send OTPs via 8x8 SMS API. ![image](../images/be14419-image.png) ## Video Demo This Video Demo shows the integration in action and explains a high level of the setup steps in this guide. We recommend referring to this text guide for the full setup. ## Requirements * Okta Account (an Okta Dev Account is fine for testing). * 8x8 Account and Subaccount with a SMS Sender. * JavaScript Knowledge for running the sample code. * API Endpoint for Okta to send a HTTP request. Required as part of their Inline Hook integration. ## Setup ### Setup Backend Code We will need an example endpoint for Okta to send it's HTTP request to. We have provided some example Node.js server code below for you to use: ```javascript server.js const express = require('express'); const bodyParser = require('body-parser'); const axios = require('axios'); const { apiKey, subaccount, sender, authKey } = require('./config'); const app = express(); const PORT = 3000; // Middleware to parse JSON bodies app.use(bodyParser.json()); // Function to send a successful response to Okta const sendSuccessResponse = (res, umid, transactionMetadata) => { res.status(200).json({ commands: [ { type: "com.okta.telephony.action", value: [ { status: "SUCCESSFUL", provider: "8x8", transactionId: umid, transactionMetadata: JSON.stringify(transactionMetadata) } ] } ] }); }; // Function to send an error response to Okta const sendErrorResponse = (res, errorData) => { const errorSummary = `8x8 Error Code: ${errorData.code}. 8x8 Error Message: ${errorData.message}`; res.status(500).json({ error: { errorSummary: errorSummary } }); }; app.post('/telephony-hook', async (req, res) => { // Print the request body console.log('Received request body:', req.body); // Check if the Authorization header is correct const authHeader = req.headers['authorization']; if (authHeader !== authKey) { console.log('Unauthorized request'); return res.status(401).json({ error: 'Unauthorized' }); } // Check if the required fields are present const { msgTemplate, phoneNumber } = req.body.data.messageProfile; if (!msgTemplate || !phoneNumber) { console.log('Invalid request data:', req.body.data); return res.status(400).json({ error: 'Invalid request data' }); } // Send OTP using 8x8's SMS API try { const response = await axios.post( `https://sms.8x8.com/api/v1/subaccounts/${subaccount}/messages`, { source: sender, destination: phoneNumber, text: msgTemplate, encoding: "AUTO" }, { headers: { 'Authorization': `Bearer ${apiKey}` } } ); // Print successful response console.log('OTP sent successfully:', response.data); // Respond with a successful delivery response sendSuccessResponse(res, response.data.umid, response.data); } catch (error) { // Print error response console.log('Failed to send OTP:', error.message); // Handle errors from the SMS API and respond with an error delivery response if (error.response && error.response.data) { sendErrorResponse(res, error.response.data); } else { sendErrorResponse(res, { code: 'UNKNOWN', message: 'An unknown error occurred' }); } } }); app.listen(PORT, () => { console.log(`Server is running on port ${PORT}`); }); ``` The code takes as input a inline hook from Okta which is simply an HTTPS request. Then it will extract the phone number and message from the request body and use that information to send an API call to 8x8's SMS API to send an OTP to the destination number. It will return either success or error information to Okta as a final step. Please note it requires parameters set in a **config.js** file as below. All values should be replaced and set with the corresponding values unique to your 8x8 Account and Okta Inline Hook configuration. The apiKey, subaccount and sender can be found in 8x8 Connect under [API Keys](https://connect.8x8.com/messaging/api-keys) and [Numbers](https://connect.8x8.com/messaging/virtual-numbers). | Key | Value | | :--------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- | | apiKey | Your 8x8 API Key. | | subaccount | Your 8x8 Subaccount. | | sender | Your 8x8 SMS Sender ID or Virtual Number | | authKey | Should be set to the same value as the Authentication Secret from the Inline Hook guide setup below. Used by your server to authenticate the request from Okta. | Here is the example **config.js** file for reference where the values should be replaced. ```json config.js // config.js module.exports = { apiKey: 'your_8x8_api_key', subaccount: 'your_8x8_subaccount', sender: 'YourSender', authKey: '1234' }; ``` Here is the example package.json for your Node.js application. It includes the packages axios, body-parser and expressed used in the backend server code. ```json package.json { "name": "okta_inline_hook_integration", "version": "1.0.0", "main": "server.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "start": "node server.js" }, "keywords": [], "author": "", "license": "ISC", "description": "", "dependencies": { "axios": "^1.7.3", "body-parser": "^1.20.2", "express": "^4.19.2" } } ``` After placing this code in the same directory, you can run the following commands to start the server. ```bash Shell - Install Packages and Start Server npm install npm start ``` The output should appear as follows. ```bash Shell - Start Server Ouptut > okta_inline_hook_integration@1.0.0 start > node server.js Server is running on port 3000 ``` The server should be exposed to the public internet so that Okta can send a webhook. The exact method is left to you but some options may include ngrok and localtunnel if you are running on your local laptop/desktop dev environment. For production, however, this should be running behind a proper web server setup. ### Create Inline Hook Go to **Workflow > Inline Hooks** on the Okta Dashboard. Select **Add Inline Hook** and **Telephony**. ![image](../images/982f180-image.png) ### Adding Inline Hook as Authenticators In the **Create Inline Hook** page, fill in the following values. | Field | Description | Example Value | |-------|-------------|---------------| | **Name** | Can be any value. We use "8x8 - SMS Authentication" | 8x8 - SMS Authentication | | **URL** | Should be set to the URL of your backend server that you send the code.For this tutorial we host it locally and use ngrok to expose it for demo purposes. In production you will need a hosting solution. | | | **Authentication Field** | Will be sent as part of the request header. Authentication Field is for your backend to authenticate the webhook from Okta. It can be any valueRefer to Okta's [page](https://developer.okta.com/docs/reference/hooks-best-practices/) on authentication and Inline Hooks for reference. | Authentication | | **Authentication Secret (Used with Authentication Field)** | Will be sent as part of the request header. This should be the value your backend uses to authenticate | secretvalue | ![image](../images/58d0026-image.png) After entering the values, click **Save**. ### Preview the Telephony Inline Hook In the next page you should see the following confirming a few of the values from setting up the Inline Hook. Select an Okta user to preview from your organization in **data.userProfile** and select anything for the **requestType**, we use MFA enrollment. ![image](../images/8ddc288-image.png) Afterwards click **Generate Request** in Step 2 on this page, it will generate an example JSON that will be sent to your endpoint so that you know what format to expect from Okta. If needed, change any of the JSON Values such as the **messageProfile.phoneNumber** field which we use in our tutorial code to send an OTP. ![image](../images/a9782e8-image.png) Click **View Response** to send the example Inline hook to your server. You should see the JSON response below from the server upon a successful request. ![image](../images/9a461a8-image.png) From our example Node.js server you should see the following output, showing the API request sent from Okta and also the output of the API call to 8x8's SMS API. ```text Server Code Output Received request body: { eventId: '3IPD5oQfQdOttCCjUWMk3Q', eventTime: '2024-08-07T22:15:30.000Z', eventType: 'com.okta.telephony.provider', eventTypeVersion: '1.0', contentType: 'application/json', cloudEventVersion: '0.1', source: '', requestType: 'com.okta.user.telephony.pre-enrollment', data: { context: { request: [Object] }, userProfile: { firstName: 'Rommel', lastName: 'Sunga', login: '', userId: '00uit3mfz9gLSSzgQ5d7' }, messageProfile: { msgTemplate: 'Your code is 11111', phoneNumber: '', otpExpires: '2024-08-07T22:20:25.057Z', deliveryChannel: 'SMS', otpCode: '11111', locale: 'EN-US' } } } OTP sent successfully: { umid: 'f68ac2f8-9da3-48d8-b8ec-f9702dad3b5b', clientMessageId: null, destination: '', encoding: 'GSM7', status: { code: 'QUEUED', description: 'SMS is accepted and queued for processing' } } ``` The SMS should also be delivered to your phone. ![image](../images/a7da876-image.png) This demonstrates the inline hook is now successfully working. Now you can attach the Okta inline hook to any action that would trigger the inline hook in Okta. ## Using the Inline Hook Now that the Inline Hook has been added, in order to require it for signing into your Okta organization. ### Add Authenticator Ensure that in the **Security - Authenticators** page that Phone is added as an Authenticator option. ![image](../images/7a5bf0e-image.png) If it is not already on the list then click **Add Authenticator** to add it. ### Create new Authentication Policy Rule Click **Add Rule** on the **Security - Authentication Policies** page. ![image](../images/c22d8d7-image.png) In the **Edit Rule** page, the only change we will make is for AND Authentication methods where we should include the **Phone - SMS** method along with any other methods we wish to offer the user authenticating into Okta. ![image](../images/c32a99f-image.png) ### Add to Application After creating the Policy, add it to one of your Applications. ![Okta Policy List](../images/37a551d-image.png "Okta Policy List") ![Inline Hook Rule - After Adding Okta Dashboard to the rule.](../images/2959b4b-image.png "Inline Hook Rule - After Adding Okta Dashboard to the rule.") ### Signing In When attempting to login to the application that you have configured above, you should receive the following screen prompting you to register for Phone Verification. ![image](../images/b65f2ca-image.png) ![image](../images/75cc45b-image.png) Again the code should be sent to your phone via SMS, follow the prompts to finish logging into application. ![image](../images/c3a7e16-image.png) For subsequent sign-ins to the application it should utilize SMS as a verification method. ## Conclusion In this tutorial we have shown how to create a Telephony Inline Hook that makes use of 8x8's SMS API to send OTPs via SMS. With this integration, you can leverage the ability of 8x8's SMS API to send an OTP while integrating with Okta. --- ## Omni Shield ## Overview Omni Shield is 8x8's solution to protect enterprises and their customers from fraudulent SMS activities, such as toll or international revenue share fraud. The Omni Shield solution offers comprehensive monitoring, real-time traffic analysis, and automatic detection and cancellation of messages from known fraudulent numbers. Its benefits include reducing Artificial Inflation of Traffic (AIT) attacks, decreasing monthly messaging expenses, and providing real-time alerts and automatic detection of potential fraud. ## How Omni Shield Works? Omni Shield Consists of two primary components: * **Traffic Anomaly Detector:** Analyse live traffic on abnormal trends using machine learning * **Phone Number Intelligence (Launching Soon):** Verify that a number is valid and responsive before sending an SMS The diagram below adds more context into how Omni Shield fits within the context of sending SMS traffic: ![Omnishield](../images/bc73c12-Omnishield.drawio_5.png) ### Recommendations To Mitigate SMS Fraud Attacks While Omni Shield can help prevent fraud attacks, we believe in a shared responsibility model between 8x8 and you as a customer. Please see the following [page](/connect/docs/recommendations-to-mitigate-sms-fraud-attacks) for recommendations that should be implemented on your end to mitigate fraud attacks. ### Glossary **Operator Share**: A metric based on the known distribution of mobile subscribers across different carriers within a country. Each mobile operator typically maintains a relatively stable percentage of the total mobile market in their country. For example, if an operator normally handles 30% of a country's mobile traffic but suddenly accounts for 60% of your application's traffic, this deviation from the expected operator share may indicate an AIT attack. **Country Share**: The distribution of your SMS traffic across different countries, which typically follows consistent patterns based on your user base and business operations. Unless you've recently expanded into new markets, significant changes in the proportion of traffic from a specific country can signal potential AIT attacks, as fraudsters often target one geographic region at a time. **Volume Spike**: An anomalous increase in SMS traffic to a specific mobile operator compared to typical patterns for that day and time of week. These spikes are analyzed in context of historical traffic patterns, taking into account normal variations due to time of day, day of week, and seasonal factors. **Conversion Rate**: The percentage of sent OTP (One-Time Password) messages that are successfully verified by users. A typical legitimate user flow involves requesting an OTP and then entering it to verify their identity. During AIT attacks, fraudsters often trigger large volumes of OTP messages without completing the verification step, resulting in an unusually low conversion rate compared to normal user behavior. This metric serves as a key indicator of potential fraudulent activity. **PNI (Phone Number Intelligence)**: 8x8 collects OTP Conversion history of a number and also checks to see if the number is flagged as suspicious by 3rd party. ### Managing Traffic Suspensions When Omni Shield detects suspicious patterns indicating an AIT attack targeting a specific operator, you can temporarily suspend message delivery to prevent further fraudulent activity. This immediate response helps protect against mounting costs while minimizing disruption to legitimate users. Key Suspension Guidelines: * Every suspension executed from 8x8 Connect has a defined expiration period to prevent accidental permanent traffic blocks * Work with our service team to analyze patterns, identify fraud sources, and develop preventive measures * Before lifting a suspension, verify the fraud source has been removed and traffic patterns have normalized * Remove the suspension promptly once resolved to restore service to legitimate users ### FAQ | Question | Answer | | --- | --- | | How do I enable Omni Shield? | Omni Shield is for existing Verificaition API Customers. If you would like to enable it for your 8x8 subaccount, reach out to your account manager. Verif8 traffic has Omni Shield enabled by default. | | Does Omni Shield work only for OTP traffic? | Yes, Omni Shield is typically enabled to detect AIT attacks in OTP traffic only. This is because OTP traffic comprises of the overwhelming majority of AIT attacks. | | Does Omni Shield automatically block messages? | Only when the service team has been given permission in advance.Otherwise, you will be notified by e-mail, and you can decide whether to block the traffic on the operator level on 8x8 Connect. | | How does Omni Shield use historical data to determine the risk of a number (and whether to block) | Omni Shield uses a few data points including:- How often an OTP converts from said number (across our platform, which means it could be coming from various brands)- 3rd party data source on how risky the number | | Can we see information about messages banned by Omni Shield on the dashboard? | This is planned within 1H 2024. For customer who need the stats today, they can be derived from the log with message status & error code. | | Can Omni Shield Protect Against all SMS Fraud? | Omni Shield is designed to curb AIT fraud specifically, but it is a shared responsibility between 8x8 and our customers using our APIs to send SMS traffic.We strongly advise you to see our section below on what you can do to prevent SMS fraud for advice to secure your platform against Fraud attacks. | | What error code is sent in an API call if a message is blocked? Will a webhook also be sent? | The message status will be "Rejected by 8x8", the error code is 99. | | In the case of number porting from one operator to another will blocking still happen? | Once a number is ported the likelihood of it being part of AIT decreases so chances are we won't block it. | --- ## Oracle Responsys [Oracle Responsys](https://www.oracle.com/sg/marketingcloud/products/cross-channel-orchestration/#:~:text=Oracle%20Responsys&text=Oracle%20Responsys%20helps%20you%20manage,Oracle%20Responsys%20can%20help%20you.) helps you manage, personalize, and orchestrate interactions across all channels to deliver timely, helpful messages in the moments that matter—all without code, complex technical training, or reliance on other experts. With 8x8’s cloud communications platform, businesses and developers alike can incorporate SMS functionality into one of their communications channels. ## Choosing 8x8 for your Oracle Responsys SMS 1. Search for SMS code in SPAN marketplace by inputing a country 2. Select Wavecell from list aggregators offerings SMS codes in the country you will be sending your message. 3. This will trigger an order for a code where we get the order and change your DR format to Responsys. 4. Wavecell will configure your credentials in SPAN configuration for the order, which will allow your requests to be accepted by Wavecell's platform. (Please see the image below) ![920](../images/5cc0b59-Oracle_1.png "Oracle 1.png") --- ## Platform Deployment Regions At 8x8 CPaaS, we understand that data latency and residency compliance are critical to delivering top-notch cloud communications. Our infrastructure leverages leading public cloud providers to store data in multiple regions, ensuring low latency and allowing you to select a location that best serves your customer base. Additionally, our geographically diverse infrastructure helps businesses meet regulatory requirements by keeping sensitive data within prescribed jurisdictions, giving you peace of mind and streamlined compliance. ## 8x8 CPaaS Platform locations | Platform Region | Description | | --- | --- | | **Asia Pacific** (default) | Our primary data center is in Singapore, delivering reliable performance across the region. | | **Europe** (EMEA) | Based in Europe to help mitigate risks associated with transferring personal data outside the UK/EEA. This facility assists businesses in complying with the GDPR, UK DPA, and offers enhanced control over data, including for the Middle East and Africa with improved latency. [More details](https://gdpr-info.eu/). | | **North America** | Our North American data center provides robust performance and low latency across the region, while helping customers comply with U.S. data protection standards and local regulatory requirements. | | **Indonesia** | Located in Indonesia to meet local regulatory requirements (GR 71 of 2019). This facility supports data protection, security mandates, and content regulations mandated for Electronic System Operators. [Learn more](https://www.dataguidance.com/notes/indonesia-data-protection-overview) . | ## Setting your Platform region * **New Account creation.** The most relevant data center to your location is automatically preselected during the new account creation process. However, we understand that your business needs may vary, so we provide you with the flexibility to modify the data center selection during that process. This empowers you to choose the location that would best align with and benefit your specific business requirements. * **Migrate existing account** *(available only to Enterprise accounts)*. Customers that have long–term contracts, account managed (enterprise) or purchase a significant amount of credits every month *(USD 10,000/month or more)* can email their account managers or our [support team](mailto:cpaas-support@8x8.com) to request a migration of account data to another geographical region. ## API Endpoints and Platform Region To ensure optimal API performance, it is essential to select the appropriate base URL for API calls corresponding to the platform deployment region where your account is provisioned. **The key advantages of correctly specifying the platform deployment region include**: * **Latency/Network Advantages:** Reduced latency in API calls is achieved when your server, initiating the API calls, is geographically closer to the selected platform region of your account. * **Data Isolation:** Complying with data residency requirements is facilitated by using the appropriate URL for your API calls, ensuring that data and logs associated with the API calls are stored in the designated region. Here is a list of available regions and their corresponding base URLs. Replace `{product}` with the relevant product name you are using. The `{product}` placeholder supports several subdomains: `sms`, `verify`, `smpp`, `lookup`, `contacts`. | Platform Region | API Base URL | | --- | --- | | Asia Pacific (default) | https://{product-name}.**8x8.com** | | Europe | https://{product-name}.**8x8.uk** | | North America | https://{product-name}.**us.8x8.com** | | Indonesia | https://{product-name}.**8x8.id** | For example, if your account is provisioned in the Indonesian region, use `https://sms.8x8.id` for your SMS API calls instead of the default `https://sms.8x8.com` which will ensure you are connecting to the Indonesian platform region directly. --- ## Price object reference 8x8 API uses the following universal object for describing the price across different APIs. Object structure | Parameter name | Parameter type | Description | | --- | --- |------------------------------------------------------------------------------------------------------------| | total | decimal\* | The total price of the message. | | perSms | decimal | Price per SMS (for SMS API only).The `total` value is equivalent to `perSms` x `smsCount`. | | currency | string\* | Currency code of price information expressed in [ISO 4217 format](https://en.wikipedia.org/wiki/ISO_4217). | > 🚧 > > Please note that the Price object is optional and might not be included in the Delivery Receipts callback. When a message was not sent successfully, you will still receive Delivery Receipts with Failed/Undelivered status without incurring any charges. Hence, the price information is not available for the Delivery Receipts. > > Examples of price object: SMS PriceChatApps Price ```json { "price": { "total": 0.0375, "perSms": 0.0125, "currency": "USD" } } ``` ```json { "price": { "total": 0.0375, "currency": "USD" } } ``` --- ## Agent Profile & Storefront When a user opens a conversation with your RCS agent, they see a verified profile page — your agent's storefront. This is what establishes trust before they read a single message. Every element shown here maps directly to a field you submit during agent registration. {/* Phone mockup */} {/* Banner */} 8x8 Let's power your CX {/* Logo */} 8x8 {/* Body */} 8x8 Demonstrate the power of RCS {/* Action buttons */} 📞Call 🌐Website ✉️Email {/* Tabs */} Info Options {/* Info rows */} 📞 (656) 221-1521 Get in Touch 🌐 https://cpaas.8x8.com/ Visit Us {/* Annotations */} {[ { label: 'Logo & banner', text: 'The circular icon and the header image. These are the first things a user sees and must be verified by the carrier.' }, { label: 'Agent name', text: 'Your verified brand name. Shown with a green verified checkmark in production once carrier vetting completes.' }, { label: 'Agent description', text: 'A short line (≤100 chars) under the name explaining what the agent does or what the user is opting into.' }, { label: 'Action buttons', text: 'Call, Website, and Email shortcuts pulled from your support contact details submitted at registration.' }, { label: 'Info tab', text: 'The full contact card: phone number, website, email, and links to your privacy policy and terms of service.' }, ].map(({ label, text }) => ( {label} {text} ))} Verified identity: Unlike SMS, RCS displays your brand name, logo, and a verified badge — so the customer knows the message is genuinely from you before they read a word. --- ## Storefront field reference | Storefront element | Registration field | Spec | | :--- | :--- | :--- | | **Agent name** | Brand / Program Name | Max 40 characters | | **Agent description** | Agent description | Max 100 characters. Describe what the agent does or what the user is opting into. | | **Tagline** | Brand slogan | Max 100 characters. A short brand phrase (separate from the description). | | **Logo** | Logo file | 224×224 px · max 50 KB · PNG or JPEG · rendered as a circle — leave padding so edges aren't cut off | | **Banner** | Hero / banner image | 1440×448 px · max 200 KB · JPEG or PNG · avoid transparent backgrounds (renders poorly in dark mode) | | **Call button** | Customer service phone + label | E.164 format, e.g. `+18668794647` | | **Website button** | Customer service website + label | Publicly accessible HTTPS URL | | **Email button** | Customer service email + label | Public support email address | | **Info tab — Privacy policy** | Privacy policy URL | Publicly accessible HTTPS URL | | **Info tab — Terms** | Terms of service URL | Publicly accessible HTTPS URL | | **Use case** | Agent use case | `Transactional`, `Promotional`, or `Multi-purpose` (see below) | --- ## Agent description vs. brand slogan These are two separate fields that are easy to confuse: - **Agent description** — tells the user what the agent *does* or what they're opting into. This is the line shown directly under your agent name on the storefront. Example: `Event alerts and live demos of 8x8 RCS capabilities` - **Brand slogan** — a short brand phrase. Example: `Explore the future of business messaging.` Both have a 100-character limit. --- ## Choosing a use case | Use case | What it covers | When to choose it | | :--- | :--- | :--- | | **Transactional** | Order receipts, booking confirmations, payment alerts, OTPs | You only send messages tied to an existing customer relationship | | **Promotional** | Marketing offers, coupons, sales alerts | You send marketing or promotional messages | | **Multi-purpose** | Both transactional and promotional | You send both types — register here to avoid re-submission later | :::caution If you register as Transactional but later send promotional messages, you will need to re-submit for approval. When in doubt, register as Multi-purpose. ::: --- ## Brand color Your brand color is used for interactive elements such as buttons and suggested replies. - **Format:** HEX code (e.g. `#E91B0C`) - **Requirement:** Must have a minimum 4.5:1 contrast ratio against white text. Verify this with a [contrast checker](https://webaim.org/resources/contrastchecker/) before submitting. --- ## Billing(Rcs) ## Global Billing | Agent Billing Category | Definition | Details | Common use cases | | :--------------------- | :--------------------------------------------------------------------------------------------------------------------------------- | :---------------------------------------------------------------------------- | :-------------------------------------------- | | Basic Message | A simple text message sent via RCS.simple text message sent via RCS. | No rich media or interactivity. Usually limited to 160 UTF-8 characters. | Alerts, OTPs, transactional notifications | | Single Message | A one-off message that may include rich media, rich cards, or suggested replies/actions. | Charged per message sent. Can include CTAs, media, or carousels. | Promotions, service updates, marketing bursts | | Conversational | Session-based pricing model. A single charge applies for unlimited two-way messaging within a defined window (typically 24 hours). | Includes rich and basic messages. Encourages real-time customer interactions. | Customer support, lead capture, chat flows | **Non-Conversational Billing Categories** Agents classified under Basic Message or Single Message billing categories are considered non-conversational. These agents are not billed per conversation. Instead, they are charged per message **What Is a Conversation in RBM?** In the context of Conversational billing, a conversation refers to a 24-hour window during which messages are exchanged between a user and a conversational agent. Only agents under the Conversational billing category can generate and be billed for conversations. **Types of Conversations** - A2P (Application-to-Person): Starts when the user replies to a message from the agent. - P2A (Person-to-Application): Starts when the agent replies to a message initiated by the user. **Conversation Window** - A conversation begins when either the agent or the user responds within 24 hours to a message from the other party, and there's no active conversation. - The conversation remains active for the next 24 hours and includes: - The initial message that triggered the reply - All messages exchanged during that 24-hour period ![diagram](../../images/e0304fb6b81fb3f81739894fe3da7e155ee540376571f6089e31c9037cfcb964-Diagram.png) **What Initiates a Billable P2A Conversation?** It is important to understand which user (P2A) interactions are considered billable "messages" that start the 24-hour conversational session. Not all user actions from an RCS message are billable. A conversation is only initiated when the user sends a message back to the agent. Actions like opening a web page or dialing a number do not count as messages and are not billable. The following table clarifies which common user actions will trigger a billable P2A session: | User Action | Considered a P2A Message? | Billable Outcome | | :------------------------------------------------------ | :------------------------ | :-------------------------------------------------------------------------------- | | Sends a freeform text response | Yes | Yes. This message initiates a single, billable 24-hour conversational session. | | Clicks a suggested response/reply | Yes | Yes. This message initiates a single, billable 24-hour conversational session. | | Sends a file (e.g., image, video) | Yes | Yes. This message initiates a single, billable 24-hour conversational session. | | Clicks a suggested action (e.g., Open URL, Dial number) | No | No. This action does not send a message and does not initiate a billable session. | | Shares location via a location push request | No | No. This action does not send a message and does not initiate a billable session. | **Conversation-Based Billing** - For agents with Conversational billing, charges apply per conversation session, not per message. - This pricing model encourages rich, two-way engagement without inflating cost per interaction. **Important Notes** - Non-conversational agents are billed per message, not per conversation, even if replies occur. - Billing data for conversational agents (e.g., logs and reports) may be delayed by up to 48 hours to ensure all messages in the session are accounted for before billing is finalised. **Important Billing Considerations** The RCS billing models described on this page represent the standard framework. However, the global messaging ecosystem is complex. Specific billing rules, rates, and the implementation of conversational sessions can sometimes vary depending on the destination country and the recipient's mobile network operator (MNO). 8x8 works to standardise these models for our customers, but underlying carrier policies can occasionally affect the final billing treatment. For the most accurate and detailed billing information applicable to your specific use cases and target regions, we strongly recommend that you speak with your 8x8 Account Manager or contact our sales team. They can provide precise details and help you forecast your messaging spend effectively. ## US Billing | Agent Billing Category | Definition | Details | Common use cases | | --- | --- | --- |----------------------| | Rich Message | A message that contains only text and a limited set of interactive actions. | - Bills in segments of 160 UTF-8 bytes. - Contains only text. No rich media is supported. - Can contain suggested replies. - Can contain the following suggested actions: - Dial phone - Open URL (without Webview) Note: A rich card containing only a title/description is not considered a text-only rich message and would be classified as a Rich Media Message." | Alerts, OTPs, transactional notifications, simple service prompts.| | Rich Media Message | A message that message that contains multimedia, text, and a full range of interactive actions. | - Charged per message sent. - Contains multimedia or text. - Media assets can be up to 100 MB. - Can contain all suggested replies and suggested actions (e.g., Open URL with Webview, Show location, Request location). Note: A message is automatically classified as a Rich Media Message if it contains any multimedia. A text-only message is also classified as a Rich Media Message if it includes suggested actions not supported by the Rich Message tier (e.g., Open URL with Webview)." | Promotions, service updates, marketing bursts, rich transactional receipts, interactive product carousels. | --- ## Compliance ## Required message templates Your agent must automatically respond to the following keywords with compliant messages. These are verified during carrier approval — missing or incorrect responses will block launch. ### CTA (Call-to-Action) / Opt-in disclosure Every opt-in touchpoint (web form, SMS keyword, in-app prompt) must include all of the following: - What the user is signing up for - Message and data rates disclosure - Message frequency disclosure - Instructions to reply STOP to opt out - A support contact (phone, email, or URL) - A link to your privacy policy **Example:** ```text Message and data rates may apply. Message frequency varies. Reply STOP to opt-out. For support, visit [URL]. Privacy Policy: [URL] ``` ### Welcome / opt-in confirmation Sent immediately after a user opts in. Must include: - Your brand name and confirmation of opt-in - Message frequency - Data rates disclosure - Instructions to reply HELP for help - Instructions to reply STOP to cancel - A customer care contact **Example:** ```text Welcome to [Brand]! You are opted in. Msg freq varies. Msg & data rates may apply. Text HELP for help, STOP to unsubscribe. For support, visit [URL]. ``` ### HELP response Sent when a user replies `HELP`. Must include: - A direct support contact — phone, email, or URL (no "we'll get back to you") - A reminder that the user can reply STOP **Example:** ```text For support, please visit [URL] or call [phone number]. To stop receiving messages, reply STOP. ``` ### STOP response Sent when a user replies `STOP`. Your agent must also handle: `QUIT`, `CANCEL`, `END`, `STOPALL`, `UNSUBSCRIBE`. Must include: - Your brand name - Confirmation that no further messages will be sent - An offer to reply START to resubscribe **Example:** ```text You have successfully unsubscribed from [Brand] messages. You will no longer receive messages. Reply START to resubscribe. ``` :::note Replace `[Brand]`, `[URL]`, and `[phone number]` with your actual values before submitting for carrier approval. ::: --- ## Opt-in & opt-out All RCS messaging must follow opt-in best practices: - Users must explicitly consent (opt-in) before receiving RCS messages. - You must document and maintain proof of consent (e.g., timestamp, source). - Include an option to opt-out (e.g., responding STOP) in your campaign design. - For interactive flows, use suggested replies for opt-out ("Stop", "Unsubscribe"). ## Prohibited Content The RCS channel must not be used to transmit restricted or inappropriate content. The following examples represent, but do not fully encompass, the types of content that are not allowed:: **Counterfeit goods** Products described as knock off, replica, imitation, clone, faux, fake, mirror image, or similar terms when referring to a brand name in an attempt to pass themselves off as genuine products of the brand owner. **Dangerous products or services** Products or services that cause damage, harm, or injury. These include, but are not limited to, illegal drugs, equipment to facilitate illegal drug use, explosive materials, fireworks, weapons, instructions for making explosives, or other harmful products. **Products, services, or content that enable dishonest behaviors** Products, services, or content that help users to mislead others such as fake documents, aids to pass drug tests, paper-writing or exam taking services; products, services, or instruction that enable unauthorized access to systems, devices, or property. **Dangerous or derogatory content** Content, products, or services that: - Incite hatred against, promote discrimination of, or disparage an individual or group on the basis of their race or ethnic origin, religion, disability, age, nationality, veteran status, sexual orientation, gender, gender identity, or other characteristic that is associated with systemic discrimination or marginalization - Harass, intimidate, or bully an individual or group of individuals - Threaten or advocate for harm on oneself or others - Seek to exploit others (e.g. blackmail, soliciting, or promoting dowries) - Inappropriate use of flags, national emblems, or religious icons and imagery **Shocking content** Content, products, or services that: - Contain violent language, gruesome or disgusting imagery, or graphic images or accounts of physical trauma - Contain gratuitous portrayal of bodily fluids or waste - Contain obscene or profane language - Likely cause shock, scare, or disgust **Capitalizing on sensitive events** Content which may be deemed as capitalizing on or lacking reasonable sensitivity towards a natural disaster, conflict, death, political violence, or other tragic event with no discernible benefit to the victims. **Animal cruelty** Content that promotes or depicts cruelty or gratuitous violence towards animals, or which may be interpreted as trading in or selling products derived from threatened or extinct species. **Adult content** Content, products, or services that are sexually explicit, sexually suggestive, or promote sexual themes, activities or escort services. Content promoting the sexual exploitation of minors (such as child sexual abuse imagery) is strictly prohibited. **Tobacco** Content, products or services that promote sales or consumption of tobacco, products containing tobacco, component parts of tobacco or products designed to simulate smoking behaviors. **Political content**Business to consumer messages (e.g., RCS Business Messages) may not include content or services related to political campaigns such as those that promote or undermine a political figure or party, conduct opinion polls or political surveys, discuss election integrity, or predict election results. Any other political content that is not prohibited by this policy must comply with local laws and regulations. **Unauthorized content** Content, products, or services that are unauthorized to use copyrighted or trademarked content, or other legally prohibited content. ## Restricted Content Some types of content may be subject to additional review or compliance measures when delivered over the RCS channel. The following categories are examples of content that may require extra scrutiny, though this list is not exhaustive: **Alcohol** Content, products, or services that promote branding, sales, promotion, or consumption of alcoholic beverages. Content that promotes irresponsible alcohol consumption is prohibited. **Gambling and games** Gambling related content, products, or services, which include but are not limited to legal gambling activities such as: physical casinos, offline and online gambling activities, national or private lottery, promotional offers for gambling sites, and social casino games. --- ## Message types and samples > **Please see [Messaging API](/connect/reference/send-message) for the full API reference.** > > ## Text Message **Content:** Text only **Character Limit:** Up to 3072 characters **Use Cases:** OTP codes, simple alerts ### Payload sample ```json { "user": { "msisdn": "+10000000000" }, "type": "Text", "content": { "text": ":wave: Hi Sarah! Just a reminder—your appointment at Wellness Dental is scheduled for tomorrow at 10:30 AM" } } ``` The corresponding message the user will receive: ![Text message example](../../images/rcs-Text.png) --- ## Sending a Rich Media Message * Media Types Supported: Images, videos, documents * File formats: .ogx, .pdf, .aac, .mp3, .mpeg, .mp3, .mp4, .mp4, .3gp, .jpeg, .jpg, .gif, .png, .h263, .m4v, .mp4, .mp4, .mpeg, .webm * Text Caption: Up to 2,000 UTF-8 characters * File Size Limits: 100MB * File URL limit: 2,048 characters ### Image & text ```json { "user": { "msisdn": "+10000000000" }, "type": "Image", "content": { "url": "https://www.example.com/image.jpg", "text": "Hi James! Your order #ORD-8821 has shipped and is on its way. Estimated delivery: tomorrow by 6 PM." } } ``` The corresponding message the user will receive: ![Image and text message example](../../images/rcs-Image_text.png) --- ### Video & text ```json { "user": { "msisdn": "+10000000000" }, "type": "Video", "content": { "url": "https://www.example.com/video.mp4", "text": "Hi Emma! Your account setup guide is ready. Watch this short video to get started in minutes." } } ``` The corresponding message the user will receive: ![Video and text message example](../../images/rcs-Video_text.png) --- ### Audio & text ```json { "user": { "msisdn": "+10000000000" }, "type": "Audio", "content": { "url": "https://www.example.com/audio.mp3", "text": "Hi Alex! You have a new voice message from our support team regarding your recent request." } } ``` The corresponding message the user will receive: ![Audio and text message example](../../images/rcs-Audio_text.png) --- ### File & text ```json { "user": { "msisdn": "+10000000000" }, "type": "Text", "content": { "url": "https://example.com/links/Invoice-october-2025.pdf", "text": "Hey John! Here's your monthly invoice for October. Contact our team if you have any questions. Thank you" } } ``` The corresponding message the user will receive: ![File and text message example](../../images/rcs-File_text.png) --- ## Rich Card A **Rich Card** bundles a title, description, a single media asset (image or video), and up to four in-card suggestions into one interactive message. Use rich cards for product promotions, appointment confirmations, order summaries, or any scenario where visual content needs to be paired with clear calls to action. **Content:** Title + description + media + in-card suggestions + optional message-level suggestions **Card Orientation:** `vertical` or `horizontal` **Media Height** (vertical cards only): `SHORT`, `MEDIUM`, or `TALL` **Use Cases:** Product showcase, booking confirmation, loyalty reward, order summary ### Rich Card — Vertical A vertical card displays the media at the top, followed by the title, description, and in-card suggestions. Use `MEDIUM` or `TALL` media height when the image is the primary content; use `SHORT` when the text is the primary content. #### Payload sample ```json { "user": { "msisdn": "+441234567890" }, "type": "RichCard", "content": { "richCard": { "cardOrientation": "vertical", "title": "Hey Melissa! New year, new shoes? 👟", "description": "Check out our latest arrivals, including the AirPulse, perfect for your next run! Plus, get a free gait analysis with any purchase this week.", "media": { "height": "SHORT", "contentInfo": { "fileUrl": "https://www.example.com/rich_card_vertical.jpg", "thumbnailUrl": "https://www.example.com/rich_card_vertical.jpg", "forceRefresh": false } }, "suggestions": [ { "reply": { "text": "🛍️ View new arrivals", "postbackData": "view_new_arrivals" } }, { "reply": { "text": "📋 Book Gait Analysis", "postbackData": "book_gait_analysis" } }, { "action": { "text": "👗 Shop now", "postbackData": "shop_now", "openUrlAction": { "url": "https://developer.8x8.com/connect/docs/rcs/message-types", "application": "WEBVIEW", "webviewViewMode": "FULL", "description": "Shop Bridgepoint Runners" }, "fallbackUrl": "https://developer.8x8.com/connect/docs/rcs/message-types" } } ] }, "suggestions": [ { "reply": { "text": "Check my orders", "postbackData": "check_my_orders" } }, { "reply": { "text": "Not interested", "postbackData": "not_interested" } } ] } } ``` The corresponding message the user will receive: ![Rich card vertical — annotated](../../images/rich-card-vertical.png) The annotations map directly to the payload: * **Media** → `content.richCard.media` * **Title text** → `content.richCard.title` * **Description text** → `content.richCard.description` * **Primary suggestions** → `content.richCard.suggestions` (in-card, up to 4) * **Secondary suggestions** → `content.suggestions` (message-level, up to 7) ### Rich Card — Horizontal A horizontal card displays the media to the left or right of the text block. Use it when the text is the primary content and the image plays a supporting role (e.g. confirmations, itineraries, compact receipts). Horizontal cards do not use `media.height` — the carrier sizes the media to the text block. #### Payload sample ```json { "user": { "msisdn": "+441234567890" }, "type": "RichCard", "content": { "richCard": { "thumbnailImageAlignment": "right", "cardOrientation": "horizontal", "title": "Your reservation at Ebi", "description": "We're springing into action to get your table ready for 5:00 PM! 🕔 Have any questions before you arrive?", "media": { "contentInfo": { "fileUrl": "https://www.example.com/rich_card_horizontal.jpg", "thumbnailUrl": "https://www.example.com/rich_card_horizontal.jpg", "forceRefresh": false } }, "suggestions": [ { "action": { "text": "Ebi location", "postbackData": "ebi_location", "viewLocationAction": { "latLong": { "latitude": 37.7749, "longitude": -122.4194 }, "label": "Ebi Restaurant" } } }, { "action": { "text": "Call us", "postbackData": "call_ebi", "dialAction": { "phoneNumber": "+12025551234" } } } ] }, "suggestions": [] } } ``` The corresponding message the user will receive: ![Rich card horizontal — Ebi reservation](../../images/rich-card-horizontal.png) ### Suggestions: in-card vs. message-level A rich card supports two separate suggestion arrays: | Location | Path | Limit | Renders as | | --- | --- | --- | --- | | **In-card** | `content.richCard.suggestions` | Up to 4 | Buttons inside the card, tied to the card content. | | **Message-level** | `content.suggestions` | Up to 7 additional | Chips below the card, for broader follow-up actions. | Together, a single message can expose up to **11** suggestions (4 in-card + 7 message-level). ### Rich Card field reference | Field | Type | Description | | --- | --- | --- | | `cardOrientation` | string | `vertical` or `horizontal`. | | `thumbnailImageAlignment` | string | Horizontal cards only. `left` or `right` — positions the media relative to the text block. | | `title` | string | Card headline. Max 200 characters. | | `description` | string | Supporting body text. Max 2 000 characters. | | `media.height` | string | `SHORT`, `MEDIUM`, or `TALL`. Vertical cards only. Optional — defaults to `SHORT` when omitted. | | `media.contentInfo.fileUrl` | string (URL) | Public URL of the image or video asset. | | `media.contentInfo.thumbnailUrl` | string (URL) | Public URL of the thumbnail (used for video). | | `media.contentInfo.forceRefresh` | boolean | If `true`, the carrier re-fetches the media instead of serving a cached copy. | | `suggestions[]` | array | In-card chips. Each entry contains either a `reply` or an `action`. | ### Limits | Field | Limit | | --- | --- | | **Title** | 200 characters | | **Description** | 2 000 characters | | **In-card suggestions** | Up to 4 chips | | **Message-level suggestions** | Up to 7 chips | | **Media file size** | Up to 100 MB | | **Card payload size** | 250 KB | ### Best practices * Keep titles short and scannable; use the description for supporting detail. * Pair every card with at least one suggestion so users have a clear next step. * Provide a `fallbackUrl` on every `openUrlAction` for clients that cannot open the webview. * Serve media over HTTPS from a stable, publicly reachable URL — the carrier may cache the asset. * Use message-level suggestions for persistent actions (e.g. "Check my orders") and keep in-card suggestions tied to the card's specific offer. --- ## Carousel A **Carousel** is a horizontally swipeable collection of rich cards delivered in a single message. Use carousels to present multiple comparable items — product variants, tour packages, available appointment slots, or menu choices — so the user can browse options inline without leaving the conversation. Each card in the carousel follows the same content model as a standalone rich card (title, description, media, in-card suggestions), but cards in a carousel share a common `cardWidth` and are always rendered vertically. **Content:** List of 2–10 cards + optional message-level suggestions **Card Width:** `small` or `medium` — applied to every card in the carousel **Use Cases:** Product catalogue, tour or package selection, menu, appointment slots, booking options ### Payload sample ```json { "user": { "msisdn": "+441234567890" }, "type": "Carousel", "content": { "carousel": { "cardWidth": "medium", "cards": [ { "title": "Catamaran day-trip", "description": "Snorkel, sushi, & sunset cocktails. $99, 6 spots left!", "media": { "height": "short", "contentInfo": { "fileUrl": "https://www.example.com/catamaran.jpg", "thumbnailUrl": "https://www.example.com/catamaran.jpg", "forceRefresh": false } }, "suggestions": [ { "action": { "text": "Buy now", "postbackData": "buy_catamaran", "openUrlAction": { "url": "https://developer.8x8.com/connect/docs/rcs/message-types", "application": "WEBVIEW", "webviewViewMode": "FULL", "description": "Buy catamaran day-trip" }, "fallbackUrl": "https://developer.8x8.com/connect/docs/rcs/message-types" } }, { "reply": { "text": "More details", "postbackData": "details_catamaran" } } ] }, { "title": "Jungle ATV tour", "description": "Explore off-road with an expert guide. $99, limited spots!", "media": { "height": "short", "contentInfo": { "fileUrl": "https://www.example.com/atv.png", "thumbnailUrl": "https://www.example.com/atv.png", "forceRefresh": false } }, "suggestions": [ { "action": { "text": "Buy now", "postbackData": "buy_atv_tour", "openUrlAction": { "url": "https://developer.8x8.com/connect/docs/rcs/message-types", "application": "WEBVIEW", "webviewViewMode": "FULL", "description": "Buy jungle ATV tour" }, "fallbackUrl": "https://developer.8x8.com/connect/docs/rcs/message-types" } }, { "reply": { "text": "More details", "postbackData": "details_atv_tour" } } ] } ] }, "suggestions": [ { "reply": { "text": "Check my orders", "postbackData": "check_my_orders" } }, { "reply": { "text": "Not interested", "postbackData": "not_interested" } } ] } } ``` The corresponding message the user will receive: ![Carousel — annotated](../../images/carousel.png) The annotations map directly to the payload: * **Rich card carousel** → `content.carousel.cards` (the swipeable cards themselves) * **Suggestion chips** → `content.suggestions` (message-level, shown below the carousel) ### Carousel field reference | Field | Type | Description | | --- | --- | --- | | `cardWidth` | string | `small` or `medium`. Applies to every card in the carousel. | | `cards[]` | array | 2 to 10 card objects. Each card has the same shape as a standalone vertical rich card, minus `cardOrientation`. | | `cards[].title` | string | Card headline. Max 200 characters. | | `cards[].description` | string | Card body. Max 2 000 characters. | | `cards[].media.height` | string | `short`, `medium`, or `tall`. Applies per card. | | `cards[].media.contentInfo.fileUrl` | string (URL) | Public URL of the image or video. | | `cards[].media.contentInfo.thumbnailUrl` | string (URL) | Public URL of the thumbnail (used for video). | | `cards[].suggestions[]` | array | In-card chips. Up to 4 per card. | | `content.suggestions[]` | array | Message-level chips shown below the carousel. Up to 7. | ### Limits | Field | Limit | | --- | --- | | **Cards per carousel** | 2 minimum, 10 maximum | | **Card title** | 200 characters | | **Card description** | 2 000 characters | | **In-card suggestions (per card)** | Up to 4 chips | | **Message-level suggestions** | Up to 7 chips | | **Media file size (per card)** | Up to 100 MB | | **Total payload size** | 250 KB | ### Best practices * Keep `cardWidth` consistent with the content density — use `small` when titles and descriptions are short; use `medium` when you need room for longer copy or larger media. * Keep the number of cards manageable. 3–5 cards typically convert best; 10 cards is the absolute ceiling. * Lead with the most relevant card — users often only browse the first two or three before making a decision. * Use the same suggestion pattern on every card (e.g. "Buy now" + "More details") so users learn the pattern quickly. * Serve media over HTTPS from a stable, publicly reachable URL — the carrier may cache the asset. * Use message-level suggestions for actions that apply to the whole conversation ("Check my orders", "Not interested") rather than to a specific card. --- ## Suggested Actions Suggestions in RCS Business Messaging provide interactive buttons, chips, or quick replies that guide users seamlessly through rich conversational experiences. By using suggestions, brands can streamline user journeys, enhance engagement, improve conversions, and gather immediate user feedback. ### Available Suggestion Types | Suggestion type | One-line description | Typical brand use cases | Core benefit | | --- | --- | --- | --- | | **Suggested Reply** | Sends a predefined text back to your agent or bot. | *Yes/No*, choose size/colour, CSAT "👍/👎", OTP confirmation. | Keeps flow structured and speeds funnel completion. | | **Dial a Number** | Opens the dialer with a preset phone number. | Escalate to live agent, click-to-call for abandoned carts, fraud alerts. | Instant voice escalation builds trust and saves high-value sales. | | **View a Location** | Launches maps focused on a given pin or search term. | Store locator, nearest ATM/locker, travel itinerary. | Drives measurable footfall from messaging. | | **Open URL / Webview** | Opens browser or in-app webview (full/half/tall). | Secure checkout, product page, claim form, loyalty sign-in. | Seamless upsell without forcing an app download. | | **Create Calendar Event** | Pre-fills a calendar entry in the user's default calendar. | Doctor appointments, flight reminders, webinar invites. | Cuts no-shows by embedding reminders directly in the calendar. | ![Suggested actions example](../../images/rcs-suggestions.png) ### Best practices * Limit to 4‑5 suggestions per message to avoid cognitive overload. * Use clear, action‑oriented labels (e.g. "Track Order" instead of "Order"). * Always set postback data so downstream systems can act on replies. * Include capability fallback (SMS or URL) when the user's client does not support a given action. * Instrument analytics to track tap‑through and optimise suggestion wording. ### Implementation Example ```json { "user": { "msisdn": "+441234567890" }, "type": "Text", "content": { "text": "👋 Hi Sarah! Just a reminder—your appointment at Wellness Dental is scheduled for tomorrow at 10:30 AM", "suggestions": [ { "reply": { "text": "Confirm", "postbackData": "user_confirmed" } }, { "reply": { "text": "Reschedule", "postbackData": "user_rescheduled" } }, { "action": { "text": "Add to Calendar", "postbackData": "add_event_to_calendar", "createCalendarEventAction": { "title": "Dental Appointment", "description": "Appointment at Wellness Dental", "startTime": "2026-02-15T10:30:00Z", "endTime": "2026-02-15T11:00:00Z" } } }, { "action": { "text": "View Location", "postbackData": "view_clinic_location", "viewLocationAction": { "latLong": { "latitude": 37.7749, "longitude": -122.4194 }, "label": "Wellness Dental" } } }, { "action": { "text": "Visit Website", "postbackData": "open_website", "openUrlAction": { "url": "https://developer.8x8.com/connect/docs/rcs/message-types", "application": "WEBVIEW", "webviewViewMode": "FULL", "description": "Visit our website" }, "fallbackUrl": "https://developer.8x8.com/connect/docs/rcs/message-types" } } ] } } ``` ## Overview of file types and limits ### Supported **File** formats are | Category | Extensions / MIME types | Notes | | --- | --- | --- | | **Images** | `.jpeg` / `.jpg` (`image/jpeg`), `.png` (`image/png`), `.gif` (`image/gif`) | Supported in rich cards & media messages | | **Video** | `.h263` (`video/h263`), `.m4v` (`video/m4v`), `.mp4` (`video/mp4`, `video/mpeg4`), `.mpeg` (`video/mpeg`), `.webm` (`video/webm`) | Supported in rich cards & media messages | | **Audio** | `.aac` (`audio/aac`), `.mp3` (`audio/mp3`, `audio/mpeg`, `audio/mpg`), `.mp4` (`audio/mp4`, `audio/mp4-latm`), `.3gp` (`audio/3gpp`), `.ogx` / `.ogg` (`application/ogg`, `audio/ogg`) | Media messages only | | **Documents** | `.pdf` (`application/pdf`) | Media messages (not rich cards) | | **File size cap** | Up to **100 MB** per attachment | | ### Limits | Message element / field | Limit | | --- | --- | | **Plain text message** | 3 072 characters | | **Rich-card title** | 200 characters | | **Rich-card description** | 2 000 characters | | **Suggested-reply text** | 25 characters | | **Suggested-action text** | 25 characters | | **Suggestion chips per message** | Up to 11 chips (4 in-card + 7 extra) | | **Carousel cards per message** | Up to 10 cards | | **Text caption with media** | 2 000 characters | | **Postback data** (per suggestion) | 2 048 characters | | **Rich-card payload size** | 250 KB | --- ## RCS(Rcs) RCS Business Messaging (RBM) is designed to enable rich, interactive communication between businesses and consumers—all within the default messaging app on their mobile devices. It's powered by Rich Communication Services (RCS), an industry protocol standardized by the GSMA (Global System for Mobile Communications Association) and adopted by mobile carriers and device manufacturers worldwide to modernise traditional SMS/MMS messaging. **Key Features:** - Branded messages with your business name, logo, and verified status. - Rich media support: Send high-quality images, videos, carousels, and file attachments. - Interactive messaging: Use buttons for calls, maps, website links, quick replies, and more. - Delivery and read receipts: Know when messages are delivered and seen. - SMS fallback: Automatically sends as SMS when RCS is unavailable on the device. **Why It's Valuable for Businesses:** - Drives higher engagement than traditional SMS with app-like experiences. - Builds trust and authenticity through verified branding. - Supports two-way conversations — ideal for updates, promotions, reminders, and customer support. ![image](../../images/25348533873ca8cdf3c734bde3024e321448d9e5e8987ae4927f62b1c96768df-Rich-card.png) --- **Key Terms:** **RCS Sender Agent** - An RCS agent or RCS Sender Agent is a digital identity that represents a brand in a customer's Rich Communication Services (RCS) messaging experience. RCS agents use the RCS Business Messaging (RBM) API to communicate with users through messages, events, and requests. **RCS** - "RCS" means the Rich Communications Services message protocol that allows users to send texts, photos, videos, and more. RCS provides a richer message feature set than the legacy SMS/MMS message protocol. **RBM** - "RBM" means RCS Business Messaging, otherwise known as non-consumer RCS and provides a messaging protocol to allow businesses to engage and interact with customers using rich, interactive message features. **Rich Media Messaging** - "Rich Media Messaging" means non-consumer text messages that include images and videos. **Basic Messaging** - "Rich Messaging" means non-consumer text-only messages. --- ## Agent Registration and Launch Launching an RCS agent requires coordination between your brand, 8x8, Google, and mobile carriers. This guide outlines the end-to-end process, from preparing your brand assets to securing carrier approval for your live agent. ## 1. Preparation & Brand Profiling Before submitting your RCS Agent Request Form, gather the following assets. Missing or incorrect formats are the most common cause of verification delays. ### A. Brand Identity Assets These define how your agent appears in the customer's native messaging app on Android and iOS. **Agent Name (Display Name):** The verified name customers will see (e.g., "Your Brand Support"). * **Constraint:** Maximum 40 characters. **Brand Color:** A HEX color code (e.g., #E91B0C) used for buttons and interactive elements. * **Requirement:** Must have a 4.5:1 contrast ratio against white text. Use a contrast checker to verify this. **Logo:** The avatar displayed next to your messages. * **Dimensions:** 224x224 px (Recommended). * **File Size:** Maximum 50 KB. * **Format:** JPG or PNG. * **Note:** This renders as a circle. Ensure your icon is centered with padding so edges aren't cut off. **Hero Image:** The banner displayed at the top of your agent's "Info & Options" page. * **Dimensions:** 1440x448 px (Aspect ratio 3.2:1). * **File Size:** Maximum 220 KB. * **Format:** JPG or PNG. * **Note:** Avoid transparent backgrounds; they may display poorly in Dark Mode. ### B. Legal & Compliance Public links are required to verify business legitimacy. * **Privacy Policy URL:** A valid, publicly accessible link. * **Terms of Service URL:** A valid, publicly accessible link. * **Contact Information:** A public email and phone number for end-user support. ### C. Use Case Definition Define your agent's primary function to ensure correct billing and approval. **Use Case Types:** * **Transactional:** Order receipts, boarding passes, payment alerts. * **Promotional:** Marketing offers, coupons, sales alerts. * **OTP:** One-time passwords. ## 2. Integration & Internal Testing Once 8x8 creates your agent profile, you must test it on real devices before requesting a public launch. **Supported Devices:** * **Android:** Most devices running Android 5.0 or later. * **iOS:** Most iPhones running iOS 18 or higher. ### The "Handshake" Process (Whitelisting) RCS agents do not "just work" on any phone during the testing phase. You must strictly follow this sequence: 1. **Request Whitelisting:** Provide the phone numbers of your test devices (in E.164 format, e.g., +14155552671) to your 8x8 account manager or support team to have them added to the authorized test list. 2. **Check for Invite:** Once added, your device will receive a native system notification (not an SMS) asking: "Make [Agent Name] a tester?". * **Action Required:** You must tap "Accept" on this notification. * **Troubleshooting:** If you skip this step, API calls will fail with a 403 PERMISSION_DENIED or "User not reachable" error because the user has not consented to receive messages from an unverified agent. ## 3. Carrier Approval & Launch Requirements Requirements vary by region. Select your target market below. ### Global (excluding US) Agents in most regions outside the US follow a streamlined path — no program brief and no carrier pre-approval are required. The STOP/HELP/START compliance rules still apply. | Step | Description | Notes | | :--- | :--- | :--- | | **1. Submit Agent Request** | Submit to initiate agent creation. Unlocks a trial sandbox immediately so you can begin testing while approvals are processed. | [Submit Agent Request Form](https://support.cpaas.8x8.com/hc/en-us/requests/new?ticket_form_id=49167509400601) | | **2. Brand Vetting** | Your brand is verified on the carrier side. | Run in parallel with Step 1 to avoid delays. Typically 5–10 business days. [Submit Brand Vetting Form](https://support.cpaas.8x8.com/hc/en-us/requests/new?ticket_form_id=49345456602137) | | **3. Carrier Demo Video** | A screen recording demonstrating the full user journey. | Must show: opt-in/welcome flow, at least one transactional message, a promotional message (if multi-purpose), and the STOP interaction. | | **4. Testing & Launch** | Whitelist test device numbers and confirm RCS is enabled on test devices. | 8x8 will guide you through this step. | :::tip You can start testing immediately after submitting the Agent Request Form — no need to wait for carrier approval. ::: ### United States For the US market, additional documentation and functionality proofs are required. ### A. Required Documentation You will need to submit the following to initiate the launch process: * **Agent Request Form:** To initiate setup. * **RCS Program Brief:** Detailed overview of campaign logic/goals. * **Brand Vetting Form:** For brand verification. * **T-Mobile Pre-approval File:** (USA Only) Specific requirement for the T-Mobile network. ### B. Functional Requirements Your agent must automatically respond to standard keywords with exact specific phrasing. Ensure your logic handles these commands before submission: **START (Welcome Message)** * **Requirement:** Must confirm opt-in, state message frequency, mention data rates, and provide instructions for Help/Stop. * **Required Output:** "Welcome! You are opted in. Msg freq varies. Msg&data rates may apply. Text HELP for help, STOP to cancel." **HELP** * **Requirement:** Must provide specific customer support contact information (phone or email). * **Required Output:** "For support, please call 1-866-879-8647." **STOP** * **Requirement:** Must confirm the opt-out and state that no further messages will be sent. * **Required Output:** "You have successfully unsubscribed. You will no longer receive messages from this agent. Reply START to resubscribe." ### C. Video Verification For approval on some carriers, you must provide a screen recording of your agent. **Video Requirement:** The video must demonstrate the full user journey: * **Opt-In:** How the user agrees to receive messages (e.g., web form, SMS keyword). * **Content:** The actual rich messages the user receives. * **STOP Flow:** The user sending "STOP" and receiving the compliant opt-out message defined above. ## 4. Go-Live & Monitoring Once approved by carriers: * **Activation:** 8x8 will activate your agent for live traffic. * **Monitoring:** Regularly review delivery rates and ensure ongoing compliance with content standards. * **Maintenance:** If you change your use case (e.g., adding promotional messages to a transactional agent), you may need to re-submit for approval. --- ## Recommendations for Securing your Traffic This page will cover both general recommendations to secure your 8x8 traffic from fraud as well as other types of security threats. Specifically for fraud attacks, we believe in a shared responsibility model between 8x8 and you as a customer. You may consider the following measures that can be leveraged to mitigate such attacks **General Best Practices** * **API Keys:** 8x8 API Keys enable backend servers to access the 8x8 API using your account's resources. It is crucial to take steps to ensure their security. * **Prevent Unauthorized Access:** Sharing API keys publicly increases the risk of unauthorized access to your APIs and the sensitive data they protect. If API keys are exposed or leaked, malicious actors can potentially abuse them to access resources, manipulate data, or launch attacks against your systems. * **Use Environment Variables:** Store API keys and other sensitive information as environment variables rather than hardcoding them directly into your code. This practice helps prevent accidental exposure through version control or code sharing. * **Rotate API Keys:** Periodically rotate API keys to mitigate potential damage in the event of a data breach. This can be done from the 8x8 Connect Dashboard by deleting old API Keys and creating new ones. * **Secure Key Distribution:** When distributing API keys to authorized users or applications, ensure secure transmission and storage practices to prevent interception, tampering, or unauthorized access. Use encrypted channels, secure protocols, and best practices for key management to protect API keys throughout their lifecycle. * **IP Whitelisting:** 8x8 is able to whitelist specific IP Addresses that we expect your API calls to originate from. If an API call for your account originals from outside those IP addresses it will be rejected. The Connect Portal allows you to specify IP addresses to whitelist. Please see the **IP Whitelisting section** on [this](/connect/docs/developer-tools#ip-whitelisting) page for further detais. * **Collect User Opt In / Opt Out:** Enable customers to opt-in/opt-out of receiving messaging content. * Optionally, you can consider implementing double opt in where the user must first input their phone number in your registration form, then they will receive an SMS to that phone number which they must respond to in order to complete the opt-in process. * ![image](../images/daaa325-image.png) * **Reconfirm number:** Customer phone numbers may change, making it important to verify their current contact information periodically (example: reconfirm SMS phone number every 3/6/12 months). * **2FA For 8x8 Connect Dashboard:** The 8x8 Connect Dashboard allows you to create/retrieve your API keys as well as send SMS directly from the Dashboard. We would recommend to use the 2FA feature of the Connect Dashboard to prevent fraudulent user access. **AIT Fraud Prevention Practices** * **Captcha:** A CAPTCHA is a type of challenge-response test used in computing to determine whether or not the user is human. We recommend our customers implement such features on their applications. * **Web Application Firewall (WAF):** Firewalls that protect web applications by filtering and monitoring HTTP traffic between a web application and the Internet. We recommend our customers deploy WAF(s) on their networks. * **Rate Limiting:** Rate limiting is a strategy for limiting network traffic. It puts a cap on how often someone can repeat an action within a certain timeframe. You can enforce rate-limiting in your service to prevent excessive traffic volume. * **IP Rate Limiting:** 8x8 offers the ability to limit how many API calls a single IP address can send with some endpoints in the 8x8 Embeddable Communications and APIs platform out of the box. You can leverage this feature to add a quick security defense in place. For more details, please see this [section] ([/connect/docs/security-1#client-ip-rate-limiting](/connect/docs/security-1#client-ip-rate-limiting)) * **MSISDN Rate Limiting:** 8x8 also offers rate limiting by the destination MSISDN. This means you can set a limit on the number of SMS messages a single MSISDN can receive in a given timeframe, such as per minute/hour/day. For instance, you can set a limit so that no MSISDN can receive more than 10 SMS messages in any 30-minute window. Importantly, this rate limiting applies universally to all MSISDNs, without the need for specifying each one. To enable this for your account: 1. Use the **Support** tab from the **Connect** Dashboard to Raise a Request. 2. Select **"General Query"** for the Request Type 3. Ask for "MSISDN Rate Limiting" in **Subject** and specify the details such as how many SMS messages to allow in what time period in the **Additional Comments** section.![image](../images/f35ec7d-image.png) * **Exponential Delays:** Implement exponential delays between failed OTP requests for the same phone number. * By Implementing an exponential delay, it makes it more difficult to exploit a user registration page for example to send a mass amount of fraudulent SMS OTPs from that page. --- ## Recommendations for Securing your Traffic(Docs) This page will cover both general recommendations to secure your 8x8 traffic from fraud as well as other types of security threats. Specifically for fraud attacks, we believe in a shared responsibility model between 8x8 and you as a customer. You may consider the following measures that can be leveraged to mitigate such attacks **General Best Practices** * **API Keys:** 8x8 API Keys enable backend servers to access the 8x8 API using your account's resources. It is crucial to take steps to ensure their security. * **Prevent Unauthorized Access:** Sharing API keys publicly increases the risk of unauthorized access to your APIs and the sensitive data they protect. If API keys are exposed or leaked, malicious actors can potentially abuse them to access resources, manipulate data, or launch attacks against your systems. * **Use Environment Variables:** Store API keys and other sensitive information as environment variables rather than hardcoding them directly into your code. This practice helps prevent accidental exposure through version control or code sharing. * **Rotate API Keys:** Periodically rotate API keys to mitigate potential damage in the event of a data breach. This can be done from the 8x8 Connect Dashboard by deleting old API Keys and creating new ones. * **Secure Key Distribution:** When distributing API keys to authorized users or applications, ensure secure transmission and storage practices to prevent interception, tampering, or unauthorized access. Use encrypted channels, secure protocols, and best practices for key management to protect API keys throughout their lifecycle. * **IP Whitelisting:** 8x8 is able to whitelist specific IP Addresses that we expect your API calls to originate from. If an API call for your account originals from outside those IP addresses it will be rejected. The Connect Portal allows you to specify IP addresses to whitelist. Please see the **IP Whitelisting section** on [this](/connect/docs/developer-tools#ip-whitelisting) page for further detais. * **Collect User Opt In / Opt Out:** Enable customers to opt-in/opt-out of receiving messaging content. * Optionally, you can consider implementing double opt in where the user must first input their phone number in your registration form, then they will receive an SMS to that phone number which they must respond to in order to complete the opt-in process. * ![image](../images/daaa325-image.png) * **Reconfirm number:** Customer phone numbers may change, making it important to verify their current contact information periodically (example: reconfirm SMS phone number every 3/6/12 months). * **2FA For 8x8 Connect Dashboard:** The 8x8 Connect Dashboard allows you to create/retrieve your API keys as well as send SMS directly from the Dashboard. We would recommend to use the 2FA feature of the Connect Dashboard to prevent fraudulent user access. **AIT Fraud Prevention Practices** * **Captcha:** A CAPTCHA is a type of challenge-response test used in computing to determine whether or not the user is human. We recommend our customers implement such features on their applications. * **Web Application Firewall (WAF):** Firewalls that protect web applications by filtering and monitoring HTTP traffic between a web application and the Internet. We recommend our customers deploy WAF(s) on their networks. * **Rate Limiting:** Rate limiting is a strategy for limiting network traffic. It puts a cap on how often someone can repeat an action within a certain timeframe. You can enforce rate-limiting in your service to prevent excessive traffic volume. * **IP Rate Limiting:** 8x8 offers the ability to limit how many API calls a single IP address can send with some endpoints in the 8x8 Embeddable Communications and APIs platform out of the box. You can leverage this feature to add a quick security defense in place. For more details, please see this [section] ([/connect/docs/security-1#client-ip-rate-limiting](/connect/docs/security-1#client-ip-rate-limiting)) * **MSISDN Rate Limiting:** 8x8 also offers rate limiting by the destination MSISDN. This means you can set a limit on the number of SMS messages a single MSISDN can receive in a given timeframe, such as per minute/hour/day. For instance, you can set a limit so that no MSISDN can receive more than 10 SMS messages in any 30-minute window. Importantly, this rate limiting applies universally to all MSISDNs, without the need for specifying each one. To enable this for your account: 1. Use the **Support** tab from the **Connect** Dashboard to Raise a Request. 2. Select **"General Query"** for the Request Type 3. Ask for "MSISDN Rate Limiting" in **Subject** and specify the details such as how many SMS messages to allow in what time period in the **Additional Comments** section.![image](../images/f35ec7d-image.png) * **Exponential Delays:** Implement exponential delays between failed OTP requests for the same phone number. * By Implementing an exponential delay, it makes it more difficult to exploit a user registration page for example to send a mass amount of fraudulent SMS OTPs from that page. --- ## Registration Module **Sender ID Registration** * Select `Registration` under the Sender ID module on your left hand side menu * Alternatively you can access the Sender ID Dashboard via URL: [https://connect.8x8.com/messaging/sender-id/registration](https://connect.8x8.com/messaging/sender-id/registration) * Sender ID registration is split into 4 major steps before a submission is made * Details - The user selects the country (currently 4 countries are offered - Indonesia, Philippines) and Headquarters (international or local). Users can select a company or add a company. * Documents—The user adds documents specific to the country and entity selected. If the user has added the company and documents via the Documents and Details module, the documents will be attached. * Sender ID details - The user can add the sender ID required (multiple Sender IDs are supported) * Review - The user can review the details before submitting the Sender ID registration. --- ## Reviewing Submission **Sender ID Registration Review** * Users will be able to view all the details for their Sender ID registration and will be able to save a draft or continue forward.(Drafts can be accessed [https://connect.8x8.com/messaging/sender-id?tab=drafts)-](https://connect.8x8.com/messaging/sender-id?tab=drafts)-) * User can also go back to any one of the steps to make any relevant changes ![image](../images/361e11fe248db2d55be159e81e61e4ca4f9e9377d8fa7e309105f8b2f7f8007b-unnamed_10.png) --- ## Salesforce Flow Builder These guides provide step-by-step instructions to integrate 8x8 Messaging APIs (e.g., WhatsApp, SMS, Viber) with Salesforce. The guides utilize Salesforce's low-code tools, like External Services and Flow Builder, and are designed for technical users such as Salesforce Administrators and Business Analysts. Learn to configure authentication, import an API specification, and build messaging automations declaratively. ### Available Guides * **[Integrating 8x8 Messaging Apps (WhatsApp, Viber, etc.) with Salesforce](/connect/docs/salesforce-flowbuilder-8x8-messaging-integration)** * A comprehensive guide to integrating the 8x8 **Messaging API**. Learn the end-to-end process, focusing on how to handle **complex, nested JSON structures**, such as those used for **WhatsApp templates**. * **[Integrating 8x8 SMS API with Salesforce](/connect/docs/salesforce-flowbuilder-8x8-sms-integration)** * A comprehensive guide to integrating the 8x8 **SMS API**. Learn the end-to-end process, including how to handle the direct and simple structure required for sending **SMS**. --- ## Sending Messages in Salesforce Flow Builder This guide will walk you through the entire process of connecting Salesforce to the 8x8 Business Messaging API (e.g. for WhatsApp) to send messages directly from your Salesforce environment from Flow Builder, by utilizing [External Services](https://help.salesforce.com/s/articleView?id=platform.external_services.htm). You can also follow the same invocable actions in Orchestrator, Einstein bots, or OmniStudio Assets. We will accomplish this using Salesforce's declarative tools, which allow you to build robust integrations without writing complex code. > 👍 **Good to know** > > While this guide focuses on WhatsApp Authentication template, you can use the same approach for other WhatsApp templates, or other [supported messaging apps](/connect/reference/list-of-supported-chatapps-channels) following their respective schema. For **SMS**, refer to [this guide](https://8x8-enterprise-group.readme.io/connect/docs/salesforce-flowbuilder-8x8-sms-integration) > > ## Prerequisites and Requirements Before you begin, ensure your Salesforce environment and 8x8 account meet the following requirements. ### **Salesforce Platform Requirements** * **Salesforce Experience:** **Lightning Experience**. * **Required Editions:** **Enterprise**, **Performance**, **Unlimited**, and **Developer** Editions. * **Feature Integration:** The actions created from an External Service can be used in declarative tools like **Flow Builder**, Orchestrator, Einstein bots, or Omnistudio. * **User Permissions Needed**: * To **define** an external service: The user needs **Modify All Data** OR **Modify Metadata Through Metadata API Functions** permissions. * To **invoke** an external service action from a flow: The user needs the **Run Flows** permission. ### **8x8 Account Requirements** * **8x8 Connect Account:** An active account with access to the messaging channels you wish to use. * **API Credentials:** Your 8x8 Subaccount ID and your API Key (Bearer Token). --- ## Part 1: The Foundation - Setting Up Authentication First, we need to teach Salesforce how to securely authenticate with the 8x8 Messaging API. This involves creating a secure chain of credentials. A Named Credential specifies the callout endpoint's URL and its required authentication parameters in one definition. ### **Step 1.1: Create the External Credential** The External Credential is a secure vault that will hold your API key. 1. Navigate to **Setup** ⚙️. In the Quick Find box, type `External Credentials` and select it. 2. Click **New**. 3. Enter the following details: * **Label**: `8x8 CPaaS Authentication` * **Name**: `8x8_CPaaS_Authentication` (this will auto-populate) * **Authentication Protocol**: Select `Custom`. 4. Click **Save**. ### Step 1.2: Create the Principal The Principal represents the specific identity and secret used for authentication. 1. On the External Credential page you just saved, scroll down to **Principals** and click **New**. 2. Enter the following details: * **Principal Name**: `8x8 API Key Principal` * **Sequence Number**: `1` 3. Under **Authentication Parameters**, click **Add Parameter**. * **Name**: `Authorization` * **Value**: `Bearer YOUR_API_TOKEN` (Replace `YOUR_API_TOKEN` with the actual API key from [this page](https://8x8-enterprise-group.readme.io/connect/docs/authentication)). 4. Click **Save**. ![Create Principal](../images/4eb73686c074945d9215cda505902cb164c71d6c36783e8c1f779021505a2da6-image.png)Create Principal ### Step 1.3: Create the Named Credential The Named Credential links the API's address (URL) to the authentication secret you just stored. 1. Navigate to **Setup** ⚙️. In the Quick Find box, type `Named Credentials` and select it. 2. Click **New**. 3. Enter the following details: * **Label**: `8x8 Messaging API` * **Name**: `8x8_Messaging_API` * **URL**: `https://chatapps.8x8.com` (Asia-Pacific deployment region; see full [list of regions](/connect/docs/platform-deployment-regions#api-endpoints-and-platform-region) * **External Credential**: Select the `8x8 CPaaS Authentication` credential you created above. * Ensure the **Generate Authorization Header** checkbox is checked. 4. Click **Save**. > ℹ️ **Sending SMS** > > For SMS, use the corresponding [sms endpoint](/connect/docs/platform-deployment-regions#api-endpoints-and-platform-region) (default is sms.8x8.com) and the appropriate SMS OAS accordingly > > ![Completed Named Credential and External Credential](../images/1bf24d3598faff200155404c07a05b4a4f16433c996d8073e27e65c49512d90c-image.png)Completed Named Credential and External Credential --- ## Part 2: Defining the API Operations Next, we will use your OAS file to teach Salesforce about the specific 8x8 API calls. 1. Navigate to **Setup** ⚙️. In the Quick Find box, type `External Services` and select it. 2. Click **Add External Service**. 3. Select **From API Specification**. 4. Configure the service: * **Service Name**: `CPaasMessagingApps` * **Named Credential**: Select the **`8x8 Messaging API`** Named Credential you just created. * **Service Schema**: Select **Upload from local** and upload the complete [OAS](https://8x8-enterprise-group.readme.io/connect/reference/messaging-apps-api-get-started) `.json` file or select "Complete Schema" and paste the schema below (contains only send single and send batch API) ```json { "openapi": "3.0.0", "info": { "title": "8x8 Send Message API (Single and Batch)", "version": "1.0", "description": "A minimal OpenAPI spec for the 8x8 Send Message and Send Message Batch operations." }, "servers": [ { "url": "https://chatapps.8x8.com", "description": "Asia-Pacific region" }, { "url": "https://chatapps.us.8x8.com", "description": "North America region" }, { "url": "https://chatapps.8x8.uk", "description": "Europe region" }, { "url": "https://chatapps.8x8.id", "description": "Indonesia region" } ], "paths": { "/api/v1/subaccounts/{subAccountId}/messages": { "post": { "summary": "Send message", "description": "This endpoint is used to send Messaging Apps messages individually (1 request per message).", "tags": [ "Send Message API" ], "operationId": "Send-Message", "security": [ { "apiKey": [] } ], "parameters": [ { "name": "subAccountId", "in": "path", "description": "You must replace *{subAccountId}* with the subaccountid that you want to use.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "description": "Messaging API: request model for send single message", "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/single-message-request" } } } }, "responses": { "200": { "description": "Success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/single-message-response" } } } }, "400": { "description": "Bad Request" }, "401": { "description": "Unauthorized" }, "500": { "description": "Internal Server Error" } } } }, "/api/v1/subaccounts/{subAccountId}/messages/batch": { "post": { "summary": "Send message batch", "description": "This endpoint is used to send Messaging Apps messages by batches (1 request for multiple messages) with personalized contents/properties. Using this API, it is possible to send up to 1,000 messages per request.", "tags": [ "Send Message API" ], "operationId": "send-message-many", "security": [ { "apiKey": [] } ], "parameters": [ { "name": "subAccountId", "in": "path", "description": "You must replace *{subAccountId}* with the subaccountid that you want to use.", "required": true, "schema": { "type": "string" } } ], "requestBody": { "description": "Messaging API: request model for send batch of messages", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/batch-message-request" } } } }, "responses": { "200": { "description": "Success response", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/batch-message-response" } } } }, "400": { "description": "Bad Request" }, "401": { "description": "Unauthorized" }, "500": { "description": "Internal Server Error" } } } } }, "components": { "securitySchemes": { "apiKey": { "type": "http", "scheme": "bearer", "description": "8x8 Messaging API accepts an ApiKey Bearer Token authentication method." } }, "schemas": { "user": { "type": "object", "properties": { "msisdn": { "type": "string" }, "country": { "type": "string" } } }, "channel-override": { "type": "object", "properties": { "channel": { "$ref": "#/components/schemas/channel-type" }, "fallbackAfter": { "type": "integer" }, "successStatus": { "$ref": "#/components/schemas/success-status" } } }, "content": { "type": "object", "properties": { "text": { "type": "string" }, "url": { "type": "string", "format": "uri" }, "fallbackText": { "type": "string" }, "sms": { "$ref": "#/components/schemas/sms-settings" }, "location": { "$ref": "#/components/schemas/location" }, "interactive": { "$ref": "#/components/schemas/interactive" }, "video": { "$ref": "#/components/schemas/video" }, "template": { "type": "object", "properties": { "name": { "type": "string" }, "language": { "type": "string" }, "components": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "header", "footer", "body", "button" ] }, "parameters": { "type": "array", "items": { "type": "object", "properties": { "type": { "type": "string", "enum": [ "text", "image", "video", "document", "location", "payload", "couponCode" ] }, "text": { "type": "string" }, "url": { "type": "string" }, "location": { "type": "object", "properties": { "latitude": { "type": "number" }, "longitude": { "type": "number" }, "name": { "type": "string" }, "address": { "type": "string" } } }, "payload": { "type": "string" }, "couponCode": { "type": "string" } } } }, "index": { "type": "integer" }, "subType": { "type": "string", "enum": [ "QuickReply", "Url", "CopyCode" ] } } } } } } } }, "content-type": { "type": "string", "enum": [ "Text", "Audio", "Video", "Image", "Location", "File", "Template" ] }, "channel-type": { "type": "string", "enum": [ "SMS", "WhatsApp", "Facebook", "RCS", "Viber", "Line", "WeChat", "Zalo", "Instagram" ] }, "success-status": { "type": "string", "enum": [ "Accepted", "Sent", "Delivered", "Read" ] }, "sms-settings": { "type": "object", "properties": { "encoding": { "type": "string" }, "source": { "type": "string" } } }, "location": { "type": "object", "properties": { "latitude": { "type": "number" }, "longitude": { "type": "number" }, "name": { "type": "string" }, "address": { "type": "string" } } }, "video": { "type": "object", "properties": { "thumbnail": { "type": "string" }, "filesize": { "type": "number" }, "duration": { "type": "number" } } }, "interactive": { "type": "object", "properties": { "type": { "type": "string", "enum": ["button", "list", "product", "product_list"] }, "action": { "type": "object" }, "header": { "type": "object" }, "body": { "type": "object" }, "footer": { "type": "object" } } }, "single-message-response": { "type": "object", "properties": { "status": { "type": "object", "properties": { "state": { "type": "string" }, "timestamp": { "type": "string", "format": "date-time" } } }, "umid": { "type": "string", "format": "uuid" }, "user": { "$ref": "#/components/schemas/user" }, "clientMessageId": { "type": "string" } } }, "batch-message-response": { "type": "object", "properties": { "batchId": { "type": "string", "format": "uuid" }, "clientBatchId": { "type": "string" }, "acceptedCount": { "type": "integer" }, "rejectedCount": { "type": "integer" }, "messages": { "type": "array", "items": { "$ref": "#/components/schemas/single-message-response" } } } }, "single-message-request": { "type": "object", "required": [ "user", "type", "content" ], "properties": { "user": { "$ref": "#/components/schemas/user" }, "clientMessageId": { "type": "string" }, "type": { "$ref": "#/components/schemas/content-type" }, "content": { "$ref": "#/components/schemas/content" }, "scheduled": { "type": "string", "format": "date-time" }, "expiry": { "type": "string", "format": "date-time" }, "dlrCallbackUrl": { "type": "string", "format": "uri" }, "channels": { "type": "array", "items": { "$ref": "#/components/schemas/channel-override" } } } }, "batch-message-request": { "type": "object", "required": [ "messages" ], "properties": { "clientBatchId": { "type": "string" }, "template": { "$ref": "#/components/schemas/batch-message-template" }, "messages": { "type": "array", "items": { "$ref": "#/components/schemas/single-message-request" } }, "includeMessagesInResponse": { "type": "boolean", "default": false } } }, "batch-message-template": { "type": "object", "properties": { "type": { "$ref": "#/components/schemas/content-type" }, "content": { "$ref": "#/components/schemas/content" }, "dlrCallbackUrl": { "type": "string", "format": "uri" }, "channels": { "type": "array", "items": {} } } } } } } ``` * Select the checkboxes next to the operation names, **Save**, then **Done**. Salesforce will now parse the file and make the `Send-Message` and `send-message-many` operations available as actions in Flow Builder. ![External Services created for both API operations](../images/0ec93a802335bbb2158049b935e607c88f91936d5af2f9ddbcf10ec18ecf622e-image.png)External Services created for both API operations --- ## Part 3: Building the Automation in Flow Builder This is where we build the logic to construct the message and send it. In this example we'll be sending a **WhatsApp Authentication template message**. ### Step 3.1: Create the Flow and Key Variables 1. Navigate to **Setup** ⚙️ > **Flows** and click **New Flow**. Select **Autolaunched Flow**. 2. From the toolbox on the left, create the following three variables by clicking **New Resource**. These will be used to build the complex request body. 3. * **Variable 1: The Main Body** * **Resource Type**: `Variable` * **API Name**: `messageBody` * **Data Type**: `Apex-Defined` * **Apex Class**: Search for and select the auto-generated class for the request body. It will be named similar to `ExternalService__CPaaSMessagingApps_singlex2dmessagex2drequest`. ![Configuring the first Variable **messageBody**](../images/6a74c3d142b83de8db2497f257f99a2781d99988844321a42c03bc83fe0de29c-image.png)Configuring the first Variable **messageBody** Repeat the same steps for the rest of the variables as shown below * **Variable 2: A Single Component** * **Resource Type**: `Variable` * **API Name**: `componentVariable` * **Data Type**: `Apex-Defined` * **Apex Class**: Search for the auto-generated class for a component. It will be named similar to `ExternalService__CPaaSMessagingApps_content_template_components`. * **Variable 3: A Single Parameter** * **Resource Type**: `Variable` * **API Name**: `parameterVariable` * **Data Type**: `Apex-Defined` * **Apex Class**: Search for the auto-generated class for a parameter. It will be named similar to `ExternalService__CPaaSMessagingApps_content_template_components_parameters`. ### Step 3.2: First Assignment - Set Core Message Properties 1. On the flow canvas, click the `+` icon after the Start element and add an **Assignment** element. 2. **Label**: `Set Core Message Body` 3. Configure the following assignments: * `{!messageBody.user.msisdn}` | `Equals` | `+6512345678` (A test phone number) * `{!messageBody.type}` | `Equals` | `template` * `{!messageBody.content.template.name}` | `Equals` | `your_authentication_template_name` * `{!messageBody.content.template.language}` | `Equals` | `en_US` ![image](../images/03372d5805bdfd5a67fd06319628e44910293d6295026235370add6628da1b05-image.png) #### Step 3.3: Second Assignment - Build the "Body" Component 1. Click the `+` on the canvas after the first assignment and add a new **Assignment** element. 2. **Label**: `Build and Add Body Component` 3. Configure the assignments to build the component from the inside out: * `{!parameterVariable.type}` | `Equals` | `text` * `{!parameterVariable.text}` | `Equals` | `123456` (Your sample OTP code) * `{!componentVariable.type}` | `Equals` | `body` * `{!componentVariable.parameters}` | **`Add`** | `{!parameterVariable}` * `{!messageBody.content.template.components}` | **`Add`** | `{!componentVariable}` ### Step 3.4: Third Assignment - Build the "Button" Component 1. Click the `+` again and add a final **Assignment** element. 2. **Label**: `Build and Add Button Component` 3. This time, we must first clear our helper variable before reusing it. Configure as follows: * `{!componentVariable.parameters}` | `Equals` | `{!$GlobalConstant.EmptyString}` * `{!parameterVariable.type}` | `Equals` | `text` * `{!parameterVariable.text}` | `Equals` | `123456` * `{!componentVariable.type}` | `Equals` | `Button` * `{!componentVariable.subType}` | `Equals` | `url` * `{!componentVariable.index}` | `Equals` | `0` * `{!componentVariable.parameters}` | **`Add`** | `{!parameterVariable}` * `{!messageBody.content.template.components}` | **`Add`** | `{!componentVariable}` ### Step 3.5: The Action - Make the API Call 1. Click the final `+` and add an **Action** element. 2. Search for your `CPaasMessagingApps` actions and select **Send-Message**. 3. **Label**: `Send WhatsApp Message` 4. Configure the inputs: * **subAccountId**: Enter your 8x8 Subaccount ID string here. * **body**: Select your main variable, `{!messageBody}`. ![image](../images/78f3891d0fda10039d2fef49ae6397c9473b2892cc24657f628b3c1b572e001d-image.png) --- ## Part 4: Permissions and Testing This final step ensures your user can run the flow and execute the callout. ### Step 4.1: Assign Permissions 1. Navigate to **Setup** ⚙️ > **Permission Sets** and create a **New** Permission Set. 2. **Label**: `CPaaS API Access` 3. In the new Permission Set, find and click on **External Credential Principal Access**. 4. Click **Edit**. Add the `8x8 CPaaS Authentication : 8x8 API Key Principal` from the available list to the enabled list. Click **Save**. 5. **Manage Assignments** for the Permission Set and assign it to your user. ### Step 4.2: Debug the Flow 1. Return to your saved Flow. 2. Click the **Debug** button. 3. Click **Run**. 4. Check the debug log on the right for a success message ("All done.") and check your phone for the WhatsApp message. You have now successfully built a low-code integration to send messages with the 8x8 Messaging API from Salesforce. --- ## Sending SMS in Salesforce Flow Builder This guide will walk you through the entire process of connecting Salesforce to the 8x8 SMS API to send messages directly from your Salesforce environment from Flow Builder, by utilizing [External Services](https://help.salesforce.com/s/articleView?id=platform.external_services.htm). You can also follow the same invocable actions in Orchestrator, Einstein bots, or OmniStudio Assets. We will accomplish this using Salesforce's declarative tools, which allow you to build robust integrations without writing complex code. ## Prerequisites and Requirements Before you begin, ensure your Salesforce environment and 8x8 account meet the following requirements. ### **Salesforce Platform Requirements** This guide utilizes Salesforce External Services. According to the official Salesforce documentation, the compatibility and permissions for this feature are as follows: * **Salesforce Experience:** Available in **Lightning Experience**. * **Required Editions:** Available in **Enterprise**, **Performance**, **Unlimited**, and **Developer** Editions. * **Feature Integration**: The actions created from an External Service can be used in declarative tools like **Flow Builder**, Orchestrator, Einstein bots, or Omnistudio. * **User Permissions Needed**: * To **define** an external service: The user needs **Modify All Data** OR **Modify Metadata Through Metadata API Functions** permissions. * To **invoke** an external service action from a flow: The user needs the **Run Flows** permission. ### **8x8 Account Requirements** * **8x8 Connect Account:** An active account with SMS services enabled. * **API Credentials:** Your 8x8 Subaccount ID and your API Key (Bearer Token), which you can generate from your 8x8 Connect customer portal. * **OpenAPI Specification (OAS) File:** You will need the OAS file for the SMS API. You can find and download it from the **[Send SMS API Reference](https://8x8-enterprise-group.readme.io/connect/docs/getting-started-with-sms-api)** page. --- ## Part 1: The Foundation - Setting Up Authentication First, we need to securely store your 8x8 API credentials in Salesforce. ### **Step 1.1: Create the External Credential** The External Credential is a secure vault for your API key. 1. Navigate to **Setup** ⚙️. In the Quick Find box, type `External Credentials` and select it. 2. Click **New**. 3. Enter the following details: * **Label**: `8x8 CPaaS Authentication` * **Name**: `X8x8_CPaaS_Authentication` * **Authentication Protocol**: Select `Custom`. 4. Click **Save**. ### **Step 1.2: Create the Principal** The Principal holds the actual secret Bearer Token. 1. On the External Credential page you just saved, scroll down to **Principals** and click **New**. 2. Enter the following details: * **Principal Name**: `8x8 API Key Principal` * **Sequence Number**: `1` 3. Under **Authentication Parameters**, click **Add Parameter**. * **Name**: `Authorization` * **Value**: `Bearer YOUR_API_TOKEN` (Replace with your actual API key). 4. Click **Save**. ![Create Principal](../images/12ff3d7e37a1acac54dda1ed67115f152e214b81055ff324617359dde97ebf0a-image.png)Create Principal ### **Step 1.3: Create the Named Credential for SMS** The Named Credential links the SMS API's specific address (URL) to the authentication secret. 1. Navigate to **Setup** ⚙️. In the Quick Find box, type `Named Credentials` and select it. 2. Click **New**. 3. Enter the following details: * **Label**: `8x8 SMS API` * **Name**: `X8x8_SMS_API` * **URL**: `https://sms.8x8.com` * **External Credential**: Select the `8x8 CPaaS Authentication` credential. * Ensure the **Generate Authorization Header** checkbox is checked. 4. Click **Save**. ![Completed Named Credential and External Credential](../images/7408ec5b970f70c3eed0a9396213ffa13f68f3b06b41090d9d3e3af9c8f458f0-image.png)Completed Named Credential and External Credential --- ## Part 2: Defining the SMS API Operations Now, we will use your SMS OAS file to teach Salesforce about the specific API call to send an SMS. 1. Navigate to **Setup** ⚙️. In the Quick Find box, type `External Services` and select it. 2. Click **Add External Service**. 3. Select **From API Specification**. 4. Configure the service: * **Service Name**: `CPaaSSMS` * **Named Credential**: Select the **`8x8 SMS API`** Named Credential you just created. * **Service Schema**: Select **Complete JSON** and upload your SMS-specific [OAS](https://8x8-enterprise-group.readme.io/connect/docs/getting-started-with-sms-api) `.json` file. * **Service Schema**: * Select Upload from local and upload the complete [OAS](https://8x8-enterprise-group.readme.io/connect/docs/getting-started-with-sms-api) `.json` file **or** * Select "Complete Schema" and paste the schema below (contains only send single SMS and send batch SMS) 5. ```json { "openapi": "3.0.1", "info": { "title": "SMS API", "description": "API to send SMS messages", "version": "1" }, "servers": [ { "url": "https://sms.8x8.com", "description": "Asia-Pacific region" }, { "url": "https://sms.us.8x8.com", "description": "North America region" }, { "url": "https://sms.8x8.uk", "description": "Europe region" }, { "url": "https://sms.8x8.id", "description": "Indonesia region" } ], "paths": { "/api/v1/subaccounts/{subAccountId}/messages": { "post": { "summary": "Send SMS", "description": "Send a single SMS message.", "operationId": "Send-Sms-Single", "parameters": [ { "name": "subAccountId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SmsRequest" } } } }, "responses": { "200": { "description": "SMS accepted and queued", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SmsResponse" } } } } }, "security": [ { "apiKey": [] } ] } }, "/api/v1/subaccounts/{subAccountId}/messages/batch": { "post": { "summary": "Send SMS Batch", "description": "Send multiple SMS messages in a single batch.", "operationId": "Send-Many-Sms", "parameters": [ { "name": "subAccountId", "in": "path", "required": true, "schema": { "type": "string" } } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BatchSmsRequest" } } } }, "responses": { "200": { "description": "Batch accepted and processed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/BatchSmsResponse" } } } } }, "security": [ { "apiKey": [] } ] } } }, "components": { "securitySchemes": { "apiKey": { "type": "http", "scheme": "bearer" } }, "schemas": { "SmsRequest": { "type": "object", "required": [ "destination", "text" ], "properties": { "destination": { "type": "string", "description": "MSISDN destination number" }, "text": { "type": "string", "description": "Message body" }, "source": { "type": "string" }, "encoding": { "type": "string", "enum": [ "AUTO", "GSM7", "UCS2" ] }, "scheduled": { "type": "string", "format": "date-time" }, "expiry": { "type": "string", "format": "date-time" } } }, "SmsStatus": { "type": "object", "properties": { "code": { "type": "string", "enum": [ "QUEUED", "REJECTED" ] }, "description": { "type": "string" } } }, "SmsResponse": { "type": "object", "required": [ "umid", "destination", "status", "encoding" ], "properties": { "umid": { "type": "string" }, "destination": { "type": "string" }, "encoding": { "type": "string", "enum": [ "AUTO", "GSM7", "UCS2" ] }, "clientMessageId": { "type": "string" }, "status": { "$ref": "#/components/schemas/SmsStatus" } } }, "BatchSmsRequest": { "type": "object", "required": [ "messages" ], "properties": { "clientBatchId": { "type": "string" }, "messages": { "type": "array", "items": { "$ref": "#/components/schemas/SmsRequest" } }, "destinations": { "type": "array", "items": { "type": "string" } }, "template": { "$ref": "#/components/schemas/SmsTemplateFull" }, "includeMessagesInResponse": { "type": "boolean" } } }, "BatchSmsResponse": { "type": "object", "required": [ "batchId", "acceptedCount", "rejectedCount" ], "properties": { "batchId": { "type": "string" }, "clientBatchId": { "type": "string" }, "acceptedCount": { "type": "integer" }, "rejectedCount": { "type": "integer" }, "messages": { "type": "array", "items": { "$ref": "#/components/schemas/SmsResponse" } } } }, "SmsTemplateFull": { "type": "object", "properties": { "source": { "type": "string" }, "text": { "type": "string" }, "encoding": { "type": "string", "enum": [ "AUTO", "GSM7", "UCS2" ] }, "scheduled": { "type": "string", "format": "date-time" }, "expiry": { "type": "string", "format": "date-time" } } } } } } ``` 6. Click **Save & Next**, 7. In **Select Operations** , select the Send-Many-Sms and Send-Sms-Single operations then **Next**, and **Finish**. Salesforce will now parse the file and make the Send-Sms-Single and Send-Many-Sms operations available as **actions** in Flow Builder. ![image](../images/78d972ceceddd9ae02eb9b596d255ce3e1ff3c8aca2619dadefb46a8842edbc9-image.png) --- ## Part 3: Building the SMS Flow The body for an SMS message is much simpler than other channels, which makes the Flow easier to build. ### **Step 3.1: Create the Flow and Body Variable** 1. Navigate to **Setup** ⚙️ > **Flows** and click **New Flow**. Select **Autolaunched Flow**. 2. From the toolbox on the left, create one variable by clicking **New Resource**. 3. * **Variable: The SMS Body** * **Resource Type**: `Variable` * **API Name**: `smsBody` * **Data Type**: `Apex-Defined` * **Apex Class**: Search for and select the auto-generated class for the SMS request body. It will be named similar to `ExternalService__CPaaSSMS_SmsRequest`. ![Configuring the first Variable **smsBody**](../images/5a61363c1af70fd59d20755320e12d5f8b2ddc950b407d3df67a360f8083a0ab-image.png)Configuring the first Variable **smsBody** ### **Step 3.2: Assignment - Set SMS Properties** We only need one Assignment element to construct the SMS message. 1. On the flow canvas, click the `+` icon after the Start element and add an **Assignment** element. 2. **Label**: `Set SMS Body` 3. Configure the following assignments. * `{!smsBody.to}` | `Equals` | `+65xxxxxxxxxx` (The recipient's phone number) * `{!smsBody.from}` | `Equals` | `8x8` (Your 8x8-registered SMS Sender ID or phone number) * `{!smsBody.text}` | `Equals` | `This is an SMS message.` ![image](../images/3dc7f853bd8a2e53f2db30df8e6a57b375c53a0dd1966872407c395801cae40d-image.png) ### **Step 3.3: The Action - Make the API Call** 1. Click the final `+` and add an **Action** element. 2. Search for your `SmsApi` actions and select the action for sending an SMS. 3. **Label**: `Send SMS` 4. Configure the inputs: * **subAccountId**: Enter your 8x8 Subaccount ID string here. * **body**: Select your main variable, `{!smsBody}`. ![image](../images/c663e80783b989de121ce72d4b7b54804e7afa5f35868e42e2a32b1a9905a5b2-image.png) --- ## Part 4: Permissions and Testing This final step ensures your user can run the flow and execute the callout. ### **Step 4.1: Assign Permissions** 1. Navigate to **Setup** ⚙️ > **Permission Sets** and create a **New** Permission Set. 2. **Label**: `CPaaS API Access` 3. In the new Permission Set, find and click on **External Credential Principal Access**. 4. Click **Edit**. Add the `8x8 CPaaS Authentication : 8x8 API Key Principal` from the available list to the enabled list. Click **Save**. 5. **Manage Assignments** for the Permission Set and assign it to your user. ### **Step 4.2: Debug the Flow** 1. Return to your saved Flow. 2. Click the **Debug** button. 3. Click **Run**. 4. Check the debug log on the right for a success message ("All done.") and check your test device for the SMS. You have now successfully built a low-code integration to send SMS with the 8x8 SMS API from Salesforce. --- ## Salesforce Chat Configuration Guide > ❗️ **Salesforce Chat Deprecation** > > Salesforce has announced that Salesforce Chat will be retired on February 14, 2026. Read the announcement and affected Salesforce editions [here](https://help.salesforce.com/s/articleView?id=001790618&type=1). > > ## Introduction This guide will take you through the necessary configurations on Salesforce and the information you need to send to 8x8 in order to get your Salesforce Chat integration working. ## Video Demo / Guide ### Viber Integration Video This Viber Integration video shows only what the integration looks like with just the Viber channel active through Chatapps API. It is the simpler scenario that this guide walks you through. ### SMS + Viber Integration Video This video shows two channels working, SMS and Viber and is a bit more complex than setting up just one channel. It also shows assigning one agent to one number, taking care of agency use cases where you always want to tie a specific agent to a phone number. ### WhatsApp Integration Video This video shows the integration with Salesforce Chat and WhatsApp. ### Prerequisites * 8x8 Connect Account * Salesforce Service Cloud Lightning account * Salesforce license with Chat (formerly known as Live Agent) ## Salesforce Integration Steps ### Queues In Salesforce Setup Home, navigate to **Administration > Users > Queues**. We will need to set up a queue for the incoming chats. These chats will be rotated between the agents associated with this queue. ![image](../images/783a5f9-Screenshot_2023-10-09_at_11.02.30_AM.png) Make sure to add the Salesforce users that you would like to be part of handling the queue here. ![image](../images/f393d42-image1.png) ### Chat In Salesforce Setup Home, navigate to **Service > Chat > Chat Settings** and check the **Enable Chat** box Note down the **Chat API Endpoint** URL to send to 8x8’s Support Team. Our team only requires the domain, which is only the first portion of the URL highlighted in red below. ![image](../images/0efaabe-image2.png) ### Chat Buttons and Invitations In the Chat Buttons and Invitations we want to create a new chat group ![image](../images/b12bfb9-image4.png) Below are the required settings. Settings outside of this are optional. **Type:** Chat Button **Name:** **Developer Name:** **Queue:** Choose the queue you created previously ![image](../images/e4b0c57-image6.png) ![image](../images/2959ebc-image7.png) Note down the **ButtonId** in the **Chat Button Code** highlighted in red to send to 8x8’s Support Team. ### Chat Deployments In Salesforce Setup Home, navigate to **Service > Chat > Deployments**, create a new deployment. ![image](../images/f5aa937-image9.png) Below are the required settings. Settings outside of this are optional. **Chat Deployment Name:** **Developer Name:** **Chat Window Title:** ![image](../images/48bc801-image8.png) Note down the **OrganizationId** and the **DeploymentId** in the **Deployment Code** highlighted in red/blue to send to 8x8’s Support Team. ![image](../images/0f7d3db-image3.png) ## 8x8 Support Team Configuration Once you have configured these settings, send an email to [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) with the following information that can be collected from the steps above: **ButtonId** - (Example: 835j000000YBZO) **DeploymentsId** - (Example: 4725j000000TxxT) **OrganisationId** - (Example: 00D4j00000D9jv3) **Chat API Endpoint** - (Example: [https://c.la2-d2-hnd.salesforceliveagent.com](https://c.la2-d2-hnd.salesforceliveagent.com)) **8x8 Subaccount** - (Can be found in 8x8 Connect Dashboard -> API Keys) **Note:** Please ensure the ButtonId and DeploymentId are from the same deployments **For Viber Support:** In addition to providing the above information, ask the support team to provision a new Viber account and assign it to your 8x8 sub account that you plan to use with this integration. ## FAQ and Known Limitations This section is intended to answer frequently asked questions (FAQ) about the integration and also share any known limitations of the integration. | Question | Answer | |---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | What happens to the messages if all agents are offline? | If all agents are offline, 8x8 will keep retrying every hour for 3 days to resend the message so it is assigned. Note that an agent will have to be online at the point in time 8x8 retries to send, it will not retry the moment an agent is online. For example if a customer sends a message at 2:00PM but no agents are online then 8x8 retries at 3:00PM. If an agent is online only at 3:01PM then the chat transcript (object created in Salesforce) will not be assigned successfully and 8x8 will retry at 4:00PM. However If an agent is only from 2:59PM onwards, then the chat transcript should be successfully assigned. Note it is not always the top of the hour that 8x8 will retry. | | Can we include custom fields? | No, currently the integration does not allow custom fields. We pass the same API request to Salesforce for all customers. | | What fields are included as part of the Chat Transcript Object & Chat Visitor that is created? | For the Chat Transcript Object, which is the main Salesforce object associated with this integration, the following fields would be available that were passed through 8x8's API call.**Deployment:** What Salesforce Deployment is Associated with the Chat Transcript**Chat Button:** What Button ID is Associated with the Chat Transcript For the Chat Visitor**Session Key:** Session ID associated with this chat session To see an example of each record, please view the section below that gives an example of each object. | | Can the look of the integration be changed? | Our integrations feeds directly into Salesforce Chat and utiliizes their UI directly. Any changes would be subject to what can be done in the Salesforce UI, we would advise to consult with either your Salesforce administrator or Salesforce Support themselves. | | Can we route via phone number? | The integration does not include phone number information in the API call and it will not be available in the Chat Transcript object. The phone number is shown in the Chat Transcript, but it is not retrievable as per our last check with Salesforce via API or in a Salesforce field itself. One potential way to route is to use multiple phone numbers for each group of agents. For example if you have a group of agents that should be reached with +65 1111 2222 and another group of agents that should be reached with +65 3333 4444, then customers can effectively be routed based on which store they have called. This will require you to create a new chat queue and associated objects per phone number. | ### Salesforce Chat Transcript Object & Chat Visitor Object Example This section gives a view of the two main objects associated with each chat session and what the agent will see when viewing them. Here is the Chat Transcript Object: ![image](../images/e187f25-image.png) Here is the Chat Visitor Object: ![image](../images/48f744b-image.png) ### Useful References [Salesforce Chat Setup Guide](https://help.salesforce.com/s/articleView?id=sf.live_agent_intro_lightning.htm&type=5) --- ## Security We know that security is important to customers. We take the responsibility to ensure that the 8x8 Embeddable Communications and APIs platform is absolutely secure, private, and reliable, so customers can have peace of mind: [Security Page on 8x8 Website](https://www.8x8.com/products/apis/security) ## **Built-in security** 8x8 proactively provides application security and authentication to all our users by building security right into our software: * Two-Factor Authentication (2FA) to the [8x8 Connect](https://connect.8x8.com/) customer portal can be achieved via the Authenticator app or SMS Verification (OTP). * 8x8 Connect supports single sign-on via SAML. * [Number Lookup API](/connect/reference/getting-started-with-number-lookup-api): Cleans user database and steps up on anti-fraud measures by checking the validity of phone numbers and their current locations. * [Mobile Verification API](/connect/reference/page): Generates and authenticates SMS-based or phone call-based mobile verification requests. * [Number Masking API](/connect/docs/getting-started-with-number-masking): Enables users to connect to a phone call while keeping their phone numbers private. * [Remove Personally Identifiable Information (PII) API](/connect/reference/delete-pii): Removes PII for particular messages from 8x8 databases. ## **Artificial Inflated Traffic (AIT, SMS Pumping, SMS Flooding)** ### **Overview** For customers who expose the API endpoints publicly and route traffics to the 8x8 Embeddable Communications and APIs platform, your endpoints might be susceptible to various attacks. As attackers increasingly automate attacks, it’s easy for them to target hundreds, if not thousands of services at once. For these reasons, it is important to understand what are the threats and how to stop them. In this section, we will discuss the risk of SMS AIT attacks specifically and what are the possible mitigations to protect your business. **1.** What is an SMS AIT attack? An SMS AIT attack occurs when a high volume of cellular SMS messages are sent to saturate and overload the website’s backend. In your normal business activity, you may allow the user to send a request to an interface that triggers an SMS message to be sent back to the user’s phone number (e.g. verification code for sign-up or sign-in). However, if there is no defense to protect the SMS interface, attackers can leverage programs to send high-frequency requests to these interfaces resulting in the following harms * Excessive SMS charges caused by malicious traffic. * User information leaks (bypass 2FA using brute-force against the account). * Performance degradation for legit users. [SMS API rate limiting](/connect/docs/api-rate-limiting) might be applied in extreme cases. * Brand reputation damage for harmed SMS recipients. #### Mitigations To protect your business from such attacks, we believe in a shared responsibility model between 8x8 and you as a customer. Please see this [page](recommendations-to-mitigate-sms-fraud-attacks) for more specific recommendations that you can implement. ### **Client IP Rate Limiting** **1.** Why do we offer rate-limiting by client IP address? The purpose of using client IP for rate limiting is to control traffic from the same origin IP that could potentially cause harm to your service. And this is a built-in feature in some APIs offered by the 8x8 platform. In your business cases, you may want to implement a simple security defense to block some common automated scripting attacks. You can leverage this feature from us to gain security capability quickly in the most cost-effective way. In the meantime, as your business grows, you can consider scaling your security with more sophisticated protection and commercial security products (like WAF) as your business needs. **2.** How to use rate-limiting with client IP There are many ways how to apply this measure in your business context. You may want to enforce the rate limit in your service locally after obtaining the actual origin IP of end-users, or you can delegate the rate-limiting to us simply by filling up the `clientIP` field with that IP address. Endpoints that support rate-limiting by clientIp are: 1. [Code generation API](/connect/reference/verify-request-v2) 2. [Send SMS API](/connect/reference/send-sms-single) 3. [Send SMS batch API](/connect/reference/send-sms-batch) To enable IP rate limiting to these endpoints for your service, you will need to do it in 2 steps: **Step 1:** Submit the request form on the [Help Center](https://support.wavecell.com/hc/en-us/requests/new?ticket_form_id=900000421766) portal. The content should be similar to the following screenshot. The customer support will help you create the IP rate limiting rule specifically to your `SubAccount` and its related endpoint. ![IP rate limiting](../images/6127379-IP_rate_limiting.png "IP rate limiting.png") **Step 2:** Fill up the `clientIp` field in the request with the origin client IP address and forward the request to 8x8 APIs. ![clientip](../images/05cddfc-clientip.png "clientip.png") **3.** Risk of IP spoofing vulnerability Please be aware that one of the common attacks to circumvent IP rate limiting is IP spoofing. Normally, an attacker sends a large amount of traffic by rotating different proxies to hide its actual origin IP. Hence, to fetch the actual origin client IP, you will need to look up the `X-Forwarded-For` header in the HTTP request if it is tunneled by a proxy. The `X-Forwarded-For` contains a list of IPs that includes proxy IP and actual origin IP addresses with the following format: ```text X-Forwarded-For: , , ``` **Examples:** ```text X-Forwarded-For: 2001:db8:85a3:8d3:1319:8a2e:370:7348 X-Forwarded-For: 203.0.113.195 X-Forwarded-For: 203.0.113.195, 70.41.3.18, 150.172.238.178 ``` It is important to parses the IP address correctly from this header, instead of always getting the first one from the list (cause it might be replaced to fake IP by a bad actor proxy). **Useful Links:** * [https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) * [https://blog.cloudflare.com/multi-user-ip-address-detection/](https://blog.cloudflare.com/multi-user-ip-address-detection/) * [https://www.f5.com/company/blog/security-rule-zero-a-warning-about-x-forwarded-for](https://www.f5.com/company/blog/security-rule-zero-a-warning-about-x-forwarded-for) * [https://www.alibabacloud.com/blog/protect-your-website-how-to-avoid-sms-traffic-flooding-attacks_65223](https://www.alibabacloud.com/blog/protect-your-website-how-to-avoid-sms-traffic-flooding-attacks_65223) * [https://cloud.google.com/architecture/rate-limiting-strategies-techniques](https://cloud.google.com/architecture/rate-limiting-strategies-techniques) * [https://www.cloudflare.com/learning/bots/what-is-rate-limiting/](https://www.cloudflare.com/learning/bots/what-is-rate-limiting/) * [https://en.wikipedia.org/wiki/CAPTCHA](https://en.wikipedia.org/wiki/CAPTCHA) --- ## Security(Docs) We know that security is important to customers. We take the responsibility to ensure that the 8x8 Embeddable Communications and APIs platform is absolutely secure, private, and reliable, so customers can have peace of mind: [Security Page on 8x8 Website](https://www.8x8.com/products/apis/security) ## **Built-in security** 8x8 proactively provides application security and authentication to all our users by building security right into our software: * Two-Factor Authentication (2FA) to the [8x8 Connect](https://connect.8x8.com/) customer portal can be achieved via the Authenticator app or SMS Verification (OTP). * 8x8 Connect supports single sign-on via SAML. * [Number Lookup API](/connect/reference/getting-started-with-number-lookup-api): Cleans user database and steps up on anti-fraud measures by checking the validity of phone numbers and their current locations. * [Mobile Verification API](/connect/reference/page): Generates and authenticates SMS-based or phone call-based mobile verification requests. * [Number Masking API](/connect/reference/title-page): Enables users to connect to a phone call while keeping their phone numbers private. * [Remove Personally Identifiable Information (PII) API](/connect/reference/delete-pii): Removes PII for particular messages from 8x8 databases. ## **SMS Flooding Attacks** ### **Overview** For customers who expose the API endpoints publicly and route traffics to the 8x8 Embeddable Communications and APIs platform, your endpoints might be susceptible to various attacks. As attackers increasingly automate attacks, it’s easy for them to target hundreds, if not thousands of services at once. For these reasons, it is important to understand what are the threats and how to stop them. In this section, we will discuss the risk of SMS flooding attacks specifically and what are the possible mitigations to protect your business. **1.** What is an SMS flooding attack? An SMS flooding attack occurs when a high volume of cellular SMS messages are sent to saturate and overload the website’s backend. In your normal business activity, you may allow the user to send a request to an interface that triggers an SMS message to be sent back to the user’s phone number (e.g. verification code for sign-up or sign-in). However, if there is no defense to protect the SMS interface, attackers can leverage programs to send high-frequency requests to these interfaces and resulting in the following harms * Excessive SMS charges caused by malicious traffic. * User information leaks (bypass 2FA using brute-force against the account). * Performance degradation for legit users. [SMS API rate limiting](/connect/docs/api-rate-limiting) might be applied in extreme cases. * Brand reputation damage for harmed SMS recipients. #### Mitigations To protect your business from such attacks, we believe in a shared responsibility model between 8x8 and you as a customer. You may consider the following measures that can be leveraged for mitigating such attacks: 1. **Captcha**: A CAPTCHA is a type of challenge-response test used in computing to determine whether or not the user is human. We recommend our customers implement such features on their applications. 2. **Web Application Firewall (WAF)**: Firewalls that protect web applications by filtering and monitoring HTTP traffic between a web application and the Internet. We recommend our customers deploy WAF(s) on their networks. 3. **Rate limiting**: Rate limiting is a strategy for limiting network traffic. It puts a cap on how often someone can repeat an action within a certain timeframe. You can enforce rate-limiting in your service to prevent excessive traffic volume. Also, we offer basic rate limiting by `clientIp` with some endpoints in the 8x8 Embeddable Communications and APIs platform out of the box. You can leverage this feature to add a quick security defense in place. We will discuss more details in the [next section] [/connect/docs/security-1#client-ip-rate-limiting](/connect/docs/security-1#client-ip-rate-limiting) ### **Client IP Rate Limiting** **1.** Why do we offer rate-limiting by client IP address? The purpose of using client IP for rate limiting is to control traffic from the same origin IP that could potentially cause harm to your service. And this is a built-in feature in some APIs offered by the 8x8 platform. In your business cases, you may want to implement a simple security defense to block some common automated scripting attacks. You can leverage this feature from us to gain security capability quickly in the most cost-effective way. In the meantime, as your business grows, you can consider scaling your security with more sophisticated protection and commercial security product (like WAF) as your business needs. **2.** How to use rate-limiting with client IP There are many ways how to apply this measure in your business context. You may want to enforce the rate limit in your service locally after obtaining the actual origin IP of end-users, or you can delegate the rate-limiting to us simply by filling up the `clientIP` field with that IP address. Endpoints that support rate-limiting by clientIp are: 1. [Code generation API](/connect/reference/verify-request-v2) 2. [Send SMS API](/connect/reference/send-sms-single) 3. [Send SMS batch API](/connect/reference/send-sms-batch) To enable IP rate limiting to these endpoints for your service, you will need to do it in 2 steps: **Step 1:** Submit the request form on the [Help Center](https://support.wavecell.com/hc/en-us/requests/new?ticket_form_id=900000421766) portal. The content should be similar to the following screenshot. The customer support will help you create the IP rate limiting rule specifically to your `SubAccount` and its related endpoint. ![1312](../images/d1fbc4f-IP_rate_limiting.png "IP rate limiting.png") **Step 2:** Fill up the `clientIp` field in the request with the origin client IP address and forward the request to 8x8 APIs. ![636](../images/63e4e1c-clientip.png "clientip.png") **3.** Risk of IP spoofing vulnerability Please be aware that one of the common attacks to circumvent IP rate limiting is IP spoofing. Normally, an attacker sends a large amount of traffic by rotating different proxies to hide its actual origin IP. Hence, to fetch the actual origin client IP, you will need to look up the `X-Forwarded-For` header in the HTTP request if it is tunneled by a proxy. The `X-Forwarded-For` contains a list of IPs that includes proxy IP and actual origin IP addresses with the following format: ```text X-Forwarded-For: , , ``` **Examples:** ```text X-Forwarded-For: 2001:db8:85a3:8d3:1319:8a2e:370:7348 X-Forwarded-For: 203.0.113.195 X-Forwarded-For: 203.0.113.195, 70.41.3.18, 150.172.238.178 ``` It is important to parses the IP address correctly from this header, instead of always getting the first one from the list (cause it might be replaced to fake IP by a bad actor proxy). **Useful Links:** * [https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For) * [https://blog.cloudflare.com/multi-user-ip-address-detection/](https://blog.cloudflare.com/multi-user-ip-address-detection/) * [https://www.f5.com/company/blog/security-rule-zero-a-warning-about-x-forwarded-for](https://www.f5.com/company/blog/security-rule-zero-a-warning-about-x-forwarded-for) * [https://www.alibabacloud.com/blog/protect-your-website-how-to-avoid-sms-traffic-flooding-attacks_65223](https://www.alibabacloud.com/blog/protect-your-website-how-to-avoid-sms-traffic-flooding-attacks_65223) * [https://cloud.google.com/architecture/rate-limiting-strategies-techniques](https://cloud.google.com/architecture/rate-limiting-strategies-techniques) * [https://www.cloudflare.com/learning/bots/what-is-rate-limiting/](https://www.cloudflare.com/learning/bots/what-is-rate-limiting/) * [https://en.wikipedia.org/wiki/CAPTCHA](https://en.wikipedia.org/wiki/CAPTCHA) --- ## Send SMS API To send a single SMS using the 8x8 SMS API you need to submit a `JSON` object to the URL `https://sms.8x8.com/api/v1/subaccounts/{subAccountId}/messages`. The JSON object has the following properties: | Property name | Property type | Description | |-----------------|---------------|---------------------------------------------------------------------------------------------------------------------| | destination | string\* | **Required**. Destination phone number. | | text | string\* | **Required**. SMS body (ie: text of the message). | | source | string | Alphanumeric or numeric string used as Sender ID for the SMS. | | clientMessageId | string | Unique id that you want to associate with the SMS (*350 chars max*) | | encoding | string | `enum`, Character set to use for this SMS. Possible values are `AUTO`, `GSM7`, `UCS2` | | scheduled | string | `timestamp`, Pre-defined date and time for this SMS to be sent in the future. | | expiry | string | `timestamp`, Maximum date and time for this SMS to be sent at | | dlrCallbackUrl | string | `uri`, Webhook URL where delivery status for the SMS will be posted (*Overwrites your default account callback URL* | ## Parameters overview Below is a more detailed description of the parameters in the SMS request body (`JSON` object submitted to the API) ### destination * This property is mandatory. * This is the destination phone number for the SMS. * The destination should be submitted following the international format and should include the country code (the leading + sign can be omitted but this is not an obligation). Valid examples: `+12025550308`, `12025550308` > 👍 > > We also accept national formats (for national you have to specify the `country` in the dedicated field). > > ### text * This property is mandatory * The text or the message (or SMS body) is the main part of your SMS: it is what is going to be displayed on the destination handset. * It can contain characters from the GSM7 character set or from the UNICODE character set (see next section for more information). * According to the encoding of the SMS, the number of SMS accounted per message will be proportional to the length of the text: * **For GSM7 messages**: * if the total length of the message is inferior or equal to 160 characters then the first and only message part can accommodate 160 characters. * If the total length is superior to 160 characters then each message part can contain 153 characters (fewer characters can be fit into one part as extra data space is taken to concatenate the SMS on the destination handset) * **For Unicode messages**: * if the total length of the message is inferior or equal to 70 characters then the first and only message part can accommodate 70 characters. * If the total length is superior to 70 characters then each message part can contain 67 characters (fewer characters can be fit into one part as extra data space is taken to concatenate the SMS on the destination handset) * For Unicode messages, 70 characters = 1 SMS (1 part) * The maximum length for a message is 10 message parts. Longer messages will be truncated to this limit and sent. ### source * This value is optional. * The source value can also be called `senderID` or TPOA. * It is the from address that will be used when delivering the SMS to the handset. * It can take different formats: * **Alphanumeric** *(example: Acme Corp):* this is the case when a source is composed of an alphanumeric string (max 11 characters: letters from the ASCII character set, digits and the space character). Alphanumeric sources are generally used for branded SMS to help the SMS receiver to identify the brand or services which originated the SMS. * **Numeric** *(example: +6512345678):* this the case when a source is composed of a string made purely of digits (max 17 chars). It can also start with the + sign. Numeric sources are generally used when the originator intends to receive an answer to the SMS as it is interpreted as a regular phone number by the destination handset. > 🚧 **Limitations** > > According to the country where the SMS is sent to, the sources can be overwritten in order to ensure better delivery. If you have some specific inquiries related to the type of source available for your account towards a specific destination, please [contact your account manager](mailto:sales-cpaas@8x8.com). > > ### clientMessageId * This value is optional. * If used, the `ClientMessageId` allows you to submit SMS associated to your custom message ID. That way, you are able to match the information contained in the API response with the ID from your own business logic. You can later read `ClientMessageId` from delivery reports. * The maximum length allowed for the `ClientMessageId` is 350 characters. ### encoding `AUTO`, `GSM7` or `UCS2` * This property is optional. The default value is `AUTO`. * **AUTO**: the API will analyze the content of your SMS text and select the correct encoding according to the characters used: if your SMS text contains UNICODE characters, then UNICODE will be selected, otherwise it will be GSM7 * **GSM7**: by using GSM7, you are forcing the encoding in use to be GSM 7 bit: it will render correctly any of the characters from the character set (See a complete list [here](https://en.wikipedia.org/wiki/GSM_03.38#GSM_7-bit_default_alphabet_and_extension_table_of_3GPP_TS_23.038_.2F_GSM_03.38)). 8x8 SMS API considers each block of 160 GSM 7 bit characters as 1 SMS unit. * **UCS2**: by using UCS2, you are forcing the encoding in use to be UNICODE: it will render correctly any of the characters from the UNICODE character set (See a complete list [here](https://en.wikipedia.org/wiki/List_of_Unicode_characters8)). 8x8 SMS API considers each block of 70 UNICODE characters as 1 SMS unit. ### scheduled * This property is optional. * The scheduled parameter should be used if you wish to schedule your message up to 7 days in advance. The SMS will be stored on the 8x8 SMS platform and sent out at the predefined time and date. You can specify the scheduling timestamp using the standard ISO 8601 format (which includes and specifies the timezone offset to use): Example: `2016-11-07T19:20:30+08:00` ### expiry * This property is optional. * The expiry parameter should be used if you wish to specify a maximum time and date for the SMS to be sent. If for any reason the 8x8 SMS platform fails to send the message before the date and time predefined, the SMS will be discarded. You can specify the expiry timestamp using the standard ISO 8601 format (which includes and specifies the timezone offset to use): Example: `2016-11-07T19:20:30+08:00` ### dlrCallbackUrl * This property is optional. * The `dlrCallbackUrl` allows specifying a webhook URL (a.k.a callback URL) that will be used to POST the delivery reports for the SMS sent in the request * If included in the request, the webhook URL in the request will be used instead of your default account webhook > 👍 > > We strongly recommend setting a default webhook (a.k.a callback URL) for your account using a [Webhooks Configuration API](/connect/reference/add-webhooks-2) > > ## Response 8x8 API returns the following response: | Property name | Property type | Description | |-----------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------| | umid | string | `uuid`, unique message ID automatically generated by 8x8 | | clientMessageId | string | Message ID that you submitted (if any) | | destination | string | Destination phone number to which the SMS was sent to | | encoding | string | Final encoding that will be used for SMS. Helpful when initial `encoding` was set to `AUTO` and you want to know detected SMS encoding. | | status | object | The object, which contains the information about a message status | Status object description | Property name | Property type | Description | |---------------|---------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | code | string | The status code can be either:- `QUEUED`: the SMS has been accepted by 8x8 SMS API and is queued for processing.- `REJECTED`: the SMS has been rejected by 8x8 SMS API and the reason is stated in the description field. It will not be processed. | | description | string | This field describes the status code and provides additional information explaining the status. | > 📘 **Downloading SMS APIs (OAS File)** > > You can download the OAS File - **[Click Here](https://github.com/8x8Cloud/public-developer-docs/blob/master/docs_oas/connect/sms_api.json)** > > ***Please do take note that the file provides all the SMS APIs so please look through the .OAS file and select the specific SMS API(s) required.*** > > --- ## Dashboard View **Linked View** By default, Sender ID Dashboard presents a “Linked View” where we display the Sender ID registration by country (destination country that the sender ID wanted to be registered towards) ![image](../images/1e04d0be7a934f182e663e1916d730152b52bb3b9ebc49670d50f1cd9a30bf52-unnamed_1.png) **Ticket View** Hovering over the column next to LOA would display “View ticket” which would allow accessing the support ticket that is associated with the particular Sender ID registration. ![View Ticket](../images/2a0ca1965393523fb5a6c4feba4291b9db7a87e06782bf20fe009d11cee88e34-View_Ticket.png) **Expanded Link View** Click on the arrow on the right side of the specific sender ID registration (as highlighted by the box in red). This allows user to track registration by mobile operator ![expanded view](../images/32e2d15021548451657f21561a64f1898e8d41f943b6d48eec609369532f80ec-expanded_view.png) Shows the registration status by a country’s operator. Track registration at the operator level ![Expanded Link View](../images/8a5501e2eb36c150cd095c2d6feafbde55f7dc68918e386a196f9ecd9111ab90-Expanded_Link_View.png) --- ## Sender ID Dashboard **Finding your Sender ID Dashboard** * Select `Registrations` under the Sender ID module on your left hand side menu * Alternatively, you can access the Sender ID Dashboard via URL: [https://connect.8x8.com/messaging/sender-id](https://connect.8x8.com/messaging/sender-id) **Sender IDs Tab** View Sender ID registration by: * Sender ID (name of sender ID that had been chosen to be registered) * Country (destination where the sender ID needed to be registered at) * Subaccount (the subaccount chosen for the Sender ID registration, this would most often be a subaccount that has the SMS product enabled) **Sorting filters** * Sender ID (sort in ascending order [a-z] or in descending order [z-a] of the Sender ID name) * Destination (sort in ascending order [a-z] or in descending order [z-a] of the destination name) * Registration Status (sort by the number of operators in the registration status) * Request Date sort in ascending order [earliest date - latest date] or in descending order [latest date - earlier date] * Category (sort in ascending order [a-z] or in descending order [z-a] of the category name) LOA (Sort by the registration status of the LOA) ![Sender ID Dashboard](../images/bb3ebb967667fb19202b5bb4bd65f2c8c1b8d9c5c914d25f9b3b7a9bb8451afd-Sender_ID_Dashboard.png) --- ## Sender ID Self Registration Module * Sender ID Self Registration Module allows user to register their Alphanumeric Sender ID which is critical for sending of SMS * First release of this Sender ID registration Service is open for Sender ID registration towards: * Indonesia (International entity\*) * Philippines (Local and International business entity) * Singapore * Thailand * Submit your Sender ID registrations and generate your Letter of Agreement in an automated manner Watch the demo [here](https://8x8.navattic.com/mg1052u) for a walkthrough on the Sender ID Self Registration Module. --- \*Entity refers to how Mobile operators classify a company. Most often companies that have a headquarters in the destination. However this is a high level guideline and is subject to change. --- ## Multi-channel Sender (MCS) Multi-channel Sender(MCS) allows users to send single or bulk SMS, Chat Apps and Voice messages. It has a capacity to let you send a message to over 500,000 contacts in a single campaign. --- ## Sending WhatsApp messages using MCS ## Sending a WhatsApp message **Here are the steps** *(For this tutorial we will highlight the method for uploading a file since it is the most popular way to send messages)* 1. Select "WhatsApp" as your channel and select your subaccount. *(This tutorial assumes you already have WhatsApp configured under your account. If you don't have one please talk to your account manager or send a request to [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com))* ![image](../images/5736d86-Screenshot_2022-10-14_at_12.24.55_AM.png "Screenshot 2022-10-14 at 12.24.55 AM.png") 2. Click "Add recipients and you will be redirected to Recipients page to enter your destination number(s). There are four(4) ways you can do this. a. Upload a file b. Type a mobile number c. Add contacts from your contact list d. Add group from your contact groups ![image](../images/d00e1a0-Screenshot_2022-10-14_at_12.25.58_AM.png "Screenshot 2022-10-14 at 12.25.58 AM.png") 3. You can upload a file by clicking the "Drag & drop to upload" icon or simply drag and drop your file. Use a .csv, .txt, or .xlsx file to upload your contacts in our default format *(you may refer to the sample file provided and we recommend that you use a .csv file format)*. 4. After you upload your contacts, you may manually amend the column labels. The default fields you can select from are Mobile, FirstName, LastName, and ClientmessageId. Alternatively, you can also add a custom field by typing the name and pressing enter. You can then select the field you just created. ![image](../images/b67359a-Screenshot_2022-10-14_at_12.28.06_AM.png "Screenshot 2022-10-14 at 12.28.06 AM.png") 5. Click "Process contacts" to review your contacts. Here, you'll be able to check all the fields you selected, country destinations, total valid numbers, or duplicates (if any). You can choose to "include duplicates" if you wish for selected contacts to receive the same message more than once. ![image](../images/cb2d8b5-Screenshot_2022-10-14_at_12.29.23_AM.png "Screenshot 2022-10-14 at 12.29.23 AM.png") 6. Click "Compose a message". For WhatsApp, it is recommended that you use an approved WhatsApp template from your WhatsApp template list. (Please click here for WhatsApp templating). ![image](../images/1a8c64a-Screenshot_2022-10-14_at_12.30.19_AM.png "Screenshot 2022-10-14 at 12.30.19 AM.png") ![image](../images/4c9ae07-Screenshot_2022-10-14_at_12.32.54_AM.png "Screenshot 2022-10-14 at 12.32.54 AM.png") 7. Some WhatsApp templates require you to fill in some parameters as shown below. ![image](../images/d185d86-Screenshot_2022-10-14_at_12.33.48_AM.png "Screenshot 2022-10-14 at 12.33.48 AM.png") 8. Once everything is ready to go, click "Send your message" and the last step will be to name your campaign which is also optional. ![image](../images/b70720c-Screenshot_2022-10-14_at_12.36.39_AM.png "Screenshot 2022-10-14 at 12.36.39 AM.png") 9. Just click "Submit" and you are done! Congratulations, you just sent your first WhatsApp message. --- ## Sending SMS using Multi-Channel Sender (MCS) ## Multi-channel Sender ![The Multi-Channel Sender (MCS) campaign creation feature in Connect](../images/7380634a5af151f2e9291fa182ca17a1cdf09d0f5803a2195a0de7037218564b-image.png)The Multi-Channel Sender (MCS) campaign creation feature in Connect The **Multi-Channel Sender (MCS)**, or just **Sender**, is a powerful campaign creation feature within Connect that lets you send various message types. You can use it to dispatch SMS, SMS Engage, WhatsApp, Viber, and Voice (text-to-speech) messages. You'll find it conveniently located in the top left navigation menu, right after "Overview." ## Video Guide Below is a video guide that covers how to send an SMS message which serves to accompany the content presented on this documentation page. ## Sending SMS You can personalise, schedule and send up to a million SMS in one go. **Steps on sending SMS** *(For this tutorial we will highlight the method for uploading a file since it is the most popular way to send SMS)* 1. **Select "SMS" as your channel and select your subaccount.** *(After you sign up, a sub-account is created and pre-selected for you. You may change this if you have different sub-accounts, which can be created by submitting a request to [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com))* ![image](../images/dfe5fa25287b3266ab2045d35686ff46e061095ee608e3a599b72e3e0fc35070-image.png) 2. **Click "Add recipients and you will be redirected to Recipients page to enter your destination number(s).** There are four(4) ways you can do this. a. Upload a file b. Type a mobile number c. Add contacts from your contact list d. Add group from your contact groups ![image](../images/2c313bbcee997b80238468b9a40c9d5b4279d308ea0f5fc29009b9890ce90ac8-image.png) 3. **You can upload your contact list** by either clicking the "Drag & drop to upload" icon or simply dragging and dropping your file directly. For best results, we recommend using a .csv file formatted according to our default layout (you can find a sample file in this step for reference). ![image](../images/67f54a21a935357bf6353f5147d16013907f08de8d0ed2ae7d59d253e2f32cb3-image.png) 4. **Once you've uploaded your contacts, you can easily adjust the column labels to match your data.** You'll find standard options like `Mobile`, `FirstName`, `LastName`, and `ClientmessageId` ready for selection. Need something more specific? Just type in a custom field name and press Enter to create and then select it.. ![image](../images/cf73b77112591b3c4edef0ffef5fc27fb70454d45245fb36ea265ba2185885e9-image.png) Creating a custom field *(From the screenshot below, the user typed and entered a new field called `Nickname`)* ![image](../images/6c59216592bcb63a8fad8188ae46adacd87e459852fbd22e32fb800702eeca4a-image.png) 5. **Click "Process contacts" to review your contact list.** On this screen, you can verify all the fields you've selected, check country destinations, and see the total valid numbers, as well as any duplicates. If you want specific contacts to receive the same message multiple times, you can choose to "include duplicates." **Handling Mixed Country Codes in Destination Lists** If your destination list has phone numbers with different country codes than your current selection for local numbers, a notification will pop up under the Destinations card. You'll get an option to force apply the selected country code to all numbers. ![image](../images/356bb136d539d20151ddfabfc89edad27e74a005b673860505332ccc3da7ee3f-image.png) 6. **Next, click "Compose a message" to start writing your message.** Before crafting your message, always enter your Sender ID. We'll automatically save it for you, so next time, just search and pick it from the dropdown list. ![image](../images/9a13de2277a48a1786bffd3b657bb1f4b03e75bf890ae6187ddff4b0ae45225a-image.png) > 📘 **Note:** > > Some countries don't support alphanumeric Sender IDs, and even when they do, pre-registration is often required. We also advise against using single-character spacing in your Sender ID, as this can negatively impact message delivery. The impact of single spacing can vary significantly between operators and countries. > > For any assistance with your Sender IDs, please reach out to our [support team](mailto:cpaas-support@8x8.com). > > Compose your message in the text area. To personalize it, simply click on a field from the "**Insert custom field**" list below. ![image](../images/80d052aadc72eb5a413388e5eb3053d98f9bea5321220e3040b87c1b4421c0fb-image.png) **SMS Character Encoding Warning** Our system helps you manage message length and cost when composing SMS. You'll see a warning **if your message contains non-GSM7 characters and uses 2 or more SMS parts**, just to raise awareness of your increased campaign cost. ![image](../images/3e3ea8ab0b4ee291f1291902cdaebb4cb59a747af5c0119ec2ff497a72c1835a-image.png) **Warning Indicators** When these conditions are met, you'll see: * A warning banner. * The text area border turns yellow. * A yellow warning icon appears next to the SMS Parts counter. ![image](../images/b4d7af8edbf552274f71288f9d9a2f74d8cc2aa8cc65e2245ab1313778594331-image.png) **What You Can Do** The warning banner won't stop you from proceeding; the "Next: Confirm and save" button remains active. You have three options: * **Close (X) button:** Hides the banner. The yellow border and warning icon remain. This isn't saved, so the warning will reappear for future messages under the same conditions. * **Remove non-GSM7 characters:** Cleans your message by removing non-GSM7 characters. The banner dismisses, the text area border resets, and the warning icon disappears. This isn't saved, so the warning will reappear for future messages under the same conditions. * **Do not show this warning again:** Hides the banner, resets the text area, and removes the warning icon. This preference is saved in your cookies, so the warning won't appear again for future messages if you're creating messages on the same device. You can also simply proceed to the next step without interacting with the banner at all. 7. Once your message is ready, click **"Send your message(s)"**. Every message you send, regardless of quantity, is treated as a campaign. A campaign name is generated for you by default, but you're welcome to change it. You'll also need to decide when to send your message: **"Send message now"** is pre-selected for immediate delivery, or you can schedule it for a later time. ![image](../images/33209f81a98d1d564e338e290e2ed2cf191f48b2b1bce256af37c54eda10b620-image.png) > ❗️ **Scheduled messages** > > *Keep in mind you can only cancel scheduled messages **up to 3 minutes before their send time**. Messages cannot be cancelled if this deadline is missed.* > > ### **Advanced SMS campaign settings - Control send speed to match your capacity** This feature gives you greater control over your SMS campaigns by helping you manage the delivery speed of your messages. It's designed to prevent a sudden flood of customer responses that could overwhelm your support team. By using this throttling mechanism, you can ensure a steady and manageable flow of customer engagement. To control the send rate, you must enable the **Limit sending speed over time** toggle in the "Confirm and save" page during SMS campaign creation for both "Send Now" or "Schedule for later" campaigns. ![image](../images/217aa6dc3c9048438dea8ff9dc6dd64c120b977bda88499bf43750a422cf13a0-image.png) Once enabled, you can configure the following fields: ![image](../images/a85a1ad210091241039eb1316672b2ca43d1ccb06b0ee0b8b51947a96913c0d3-image.png) * **Number of messages**: Set the maximum number of messages to be sent within a specific time unit. * **Time Unit**: Choose from a dropdown menu with options: Minute, Hour, or Day. This selection determines the rate at which messages are sent. * **Delivery Window**: Set a Start Time and End Time to define the hours when messages will be sent. This is useful if you don’t want to disturb your audience during the night or if you want to send messages during a specific time window for optimal conversions.
The start and end times will be set according to the time zone of the capital city of your recipients’ countries. ![image](../images/19bd70a581f69d25ee5d3c161c5b9eb63ba8407351fe94bac5b964dc7aab0cbf-image.png) * **Days**: The system defaults to 5 days starting from the campaign creation or scheduled date. You can select or deselect days within this dynamic five-day window. The maximum duration for message throttling is a **120-hour (5-day) delivery window.** > 📘 Message sending limits > > If your configured send rate won't reach all recipients within the 120-hour delivery window, a validation error will appear, and the "Submit" button will be disabled. > > For example, if you set a rate of **2 messages per day for 3 days to a list of 10 recipients**, only **6 messages** will be sent, meaning your campaign will be incomplete. To fix this, you can increase the number of messages, change the time unit, extend the delivery window, or reduce the total number of recipients (e.g. split the campaign into multiple smaller ones) > > ![image](../images/f68778518a3dd935cbb2219811bfe6d22749c34359e00488594ef428945e7d59-image.png) 8. Click "Send" to process and send your message. If you need to re-enter all the fields, simply click "Cancel". 9. After submitting, you'll be redirected to the list of Campaigns where you can see the last campaign created and its status right at the top of the list. ![image](../images/15f931d33ee5e5f9a0b2ba13f75864118443158285a399af387c9360cb74abbe-image.png) > 📘 Campaign status with controlled sending speed over time > > After you submit a campaign that has limited sending speed over time (see above feature), it's added to the Campaign list with a "Processing" status. > > The campaign details page may show an empty state if messages haven't started sending yet. Once messages begin to send, a partial state will be displayed, including a "Delivery in progress" note and a "Partial" cost label showing the running cost. > > ![image](../images/1574fe0500622dd4e842408fa17ca9c722f1e217ada93c37e93192fe5dd94888-image.png) > > ![image](../images/f6264d6559c46e65b8d77c73ee2a338c96154c4421877bf655726b1c580c59d8-image.png) > > **Message Status Update Timing** > All messages campaigns will have their status updated every **5 mins for their first day**. After that, on the second and succeeding days, status will only be updated once a day at 18:00 UTC (2:00 AM Singapore Time). --- ## Short URL Clicks 8x8 SMS API provides a webhook that notifies you whenever someone clicks a shortened URL in an SMS message sent from your sub-account. If URL shortening is enabled for your sub-account, any links in your messages are automatically converted to short URLs. When a recipient clicks one of those links, 8x8 sends a `POST` request to your configured webhook endpoint with details about the click. ### Requirements To use the Short URL Click webhook feature, coordinate with your account manager to activate the following: - Short URL feature enabled for your sub-account: allows 8x8 to automatically shorten links in your outbound SMS. - Short URL Click webhook: the callback URL where 8x8 will send click events whenever a recipient clicks a shortened link in your SMS. > 📘 > You can configure your callback using [Webhooks Configuration API](/connect/reference/get-webhooks-2) ### Webhook format #### Request body description | Parameter name | Parameter type | Description | | --- | --- | --- | | namespace | string | A generic namespace for incoming webhook.Equal to `SMS`. | | eventType | string | Webhook type.Equals to `short_url_clicked`. | | description | string | Human-readable description of the event. | | payload | object | Contains the short URL click information. | #### Payload object description | Parameter name | Parameter type | Description | | :------------- | :------------- | :----------- | | eventId | uuid | Unique identifier for the click event. | | occurredAt | string | UTC timestamp of when the click occurred (ISO 8601). | | umid | uuid | Unique ID of the original SMS message. | | destination | string | Phone number of the recipient (E.164 format). | | shortUrl | string | The shortened URL that was clicked. | | targetUrl | string | The final destination URL. | --- ### Sample webhook body ```json { "namespace": "SMS", "eventType": "short_url_clicked", "description": "Short URL from SMS clicked", "payload": { "eventId": "0f6a0c0f-8b67-4a3a-a6c3-7b0ecf0d2b1a", "occurredAt": "2025-10-03T12:00:00Z", "umid": "9e09ac86-bd74-5465-851d-1eb5a5fdbb9a", "destination": "+12025550293", "shortUrl": "https://2g.to/abc123/abc", "targetUrl": "https://example.com/landing?c=fall_campaign" } } ``` ### Retry logic In case of connection error/timeout or HTTP response code 4XX or 5XX, there will be multiple retry attempts with progressive intervals: 1, 10, 30, 90 sec. --- ## Signing LOA **LOA Signing** * An email would be sent to your email address informing you that your ticket has been updated and that your LOA is ready for Signing * View your LOA submission that is ready for signing by accessing your Sender ID dashboard - [https://connect.8x8.com/messaging/sender-id/registration](https://connect.8x8.com/messaging/sender-id/registration) * Click on `Sign LOA` to trigger the signing process ![image](../images/ffa87fe2449891b3fce89870026de04254ccc065b55d768d6f0a4d057d1dbbc6-unnamed_14.png) ![image](../images/6296e5620d078fed2177eca28c0121589c37e9fd16be6fb445f12b404a4efc5c-unnamed_15.png) * User has an option to sign via Docusign(where applicable) and Wet Signature (requires user to download the file, sign and upload it back ![image](../images/acdd0fe3607d7513a4fbd7c96d3d949088f6b359a15680bed011fec949674f14-unnamed_17.png) * Wet Signature requires user to download and sign, an uploader will be seen once the user downloads the LOA ![image](../images/792fb0a68e3d462133785775276a39f45ee85ca74e87724a10218e898a99f09e-wet_signature_download_LOA.png) ![image](../images/59cd63388d609447e0c234380ac248c966c27b1b2336f274e56313a64097fd33-Screenshot_2024-12-10_at_6.32.59_PM.png) --- ## SMPP - Connection 8x8 supports SMPP (Short Message Peer-to-Peer), a mature binary protocol widely used in carrier-grade SMS infrastructure. Unlike REST APIs, SMPP uses persistent TCP connections and is designed for sustained, high-volume message throughput with low latency. This connection method is a good fit if you are running enterprise messaging software, an SMS gateway, or any platform that natively speaks SMPP. For new integrations without an existing SMPP requirement, the [8x8 SMS API](/connect/docs/getting-started-with-sms-api) offers a simpler REST-based alternative. The 8x8 SMPP environment runs on a high-availability cluster designed for enterprise-scale traffic. Connecting to our regional hostnames provides automatic failover and intelligent load balancing to ensure optimal performance. ### Protocol Specification > **Version:** SMPP v3.4 > **Format:** All PDUs must conform to the standard v3.4 binary specification. --- ## Connection Details The hostname depends on the [platform deployment region](/connect/docs/platform-deployment-regions) your account is provisioned in. | Setting | Value | | --- | --- | | Hostname (Asia Pacific) | smpp.8x8.com | | Hostname (Europe) | smpp.8x8.uk | | Port | 2775 (Legacy/Non-Secure) | | Port (TLS) | 2776 (TLS v1.3) | | system_id | your username | | password | your password | ### Security & Compliance * **IP Whitelisting:** For enhanced security, SMPP binds are strictly controlled via IP Whitelisting. Please provide your source IP addresses to your account manager during onboarding to enable access. * **Encryption:** Port 2775 is maintained for legacy compatibility and transmits in plaintext. Use **Port 2776 (TLS v1.3)** for all production environments to ensure data integrity. * **IPSec:** For enhanced network-level security, 8x8 also supports **IPSec Tunnel** binds. Contact [support](mailto:cpaas-support@8x8.com) for setup details. --- ## Binding & Architecture A bind is a persistent TCP session between your application and the 8x8 SMPP server. The bind type determines the direction of message flow: * **`bind_transmitter`**: Dedicated to sending messages (MT traffic). * **`bind_receiver`**: Dedicated to receiving messages and delivery receipts (MO and DLRs). * **`bind_transceiver`**: Supports bidirectional traffic on a single connection. ### Performance Optimization For high-throughput integrations, we recommend splitting traffic into dedicated **transmitter** and **receiver** binds. This architecture prevents "head-of-line blocking," ensuring that a high volume of outbound submissions (`submit_sm`) does not delay the processing of inbound delivery receipts (`deliver_sm`). ### Session Management * **Capacity:** Accounts are typically provisioned with a baseline of **4 concurrent binds** per `system_id`. * **Elasticity:** For customers requiring higher parallelization, additional binds can be allocated to meet your architecture's needs. * **DLR Routing:** 8x8 intelligently routes delivery receipts (`deliver_sm`) to any active bind sharing the same `system_id`, ensuring consistent updates regardless of which session originated the message. --- ## Throughput & Performance The 8x8 platform is engineered to handle massive, carrier-grade message volumes. To ensure a smooth integration, we apply the following performance baselines: * **Baseline Throughput:** By default, connections start at **50 messages per second (MPS) per bind**. * **Enterprise Scaling:** We routinely scale MPS limits significantly higher for high-volume customers. Your account manager can adjust these limits to match your specific production requirements. * **Asynchronous Windowing:** To achieve maximum performance, we recommend using an asynchronous windowing approach, allowing you to submit multiple PDUs without waiting for individual responses. --- ## Supported PDUs | PDU | Description | | --- | --- | | `bind_*` | Authenticate and establish the session | | `submit_sm` | Submit a short message for delivery | | `enquire_link` | Keep-alive heartbeat to maintain the session | | `deliver_sm_resp` | Acknowledge a received `deliver_sm` (DLR) | | `unbind` | Gracefully terminate the session | --- ## Data Encoding When sending messages, set the correct Data Coding Scheme (DCS) value in your `submit_sm` PDU: | DCS Value | Encoding | | --- | --- | | 0 or 1 | GSM7 (default) | | 3 | Latin-1 (ISO-8859-1) | | 8 | Unicode (UCS-2) | --- ## SMPP - Delivery receipts 8x8 sends delivery report information in the `short_message` field of a `deliver_sm` PDU. The following format should be expected: ## Format ```text id:IIIIIIIIII sub:SSS dlvrd:DDD submit date:YYMMDDhhmm done date:YYMMDDhhmm stat:DDDDDDD err:E Text: . . . . . . . . . ``` Where: * `stat`: is one of the message states below * `err`: is one of the error codes below, if available. ## Message States The following message states can be found in a delivery report: * `DELIVRD` * `EXPIRED` * `UNDELIV` * `ACCEPTD` * `UNKNOWN` * `ENROUTE` * `REJECTD` ## Error Codes For the list of error codes that might be sent in DLR please refer to [SMS Delivery receipts error codes](/connect/reference/delivery-receipts-error-codes) --- ## SMPP TLVs ### SMS - SMPP TLVs 📘Optional Parameters are fields, which may be optionally included in an SMPP message. (SMPP v3.4 Spec, section 5.3). These fields contain 3 parts, namely Tag, Length and Value. Some TLVs are defined by the SMPP protocol and then there is room for vendors to create their own TLVs. Below are the vendor specific TLVs defined by 8x8. These TLVs are supported only when using SMPP to make requests to 8x8 --- ### Mo Message Id A unique 36 alphanumeric character string (UUID v4) to identify the MO message at the SMSC. This message ID can be used when contacting 8x8 support regarding the message. | Field | Sized Octets | Type | Description | | --- | --- | --- |-------------------------------------------------------------------------------------------------------------------------------------------------------| | Tag | 2 | Integer | Equal to **0x1502** (5378 Decimal) | | Length | 2 | Integer | Equal to 0x0024 (36 Decimal)This means the size of the `value` is 36 bytes | | Value | 36 | Octet String | Octet string of 36 characters. This is a UUID v4 value which represents the 8x8 unique message Id (UMID)Note that this is NOT null-terminated | --- ### Destination Mcc and Destination Mnc Mobile Country Code (mcc) and the Mobile Network Code (mnc) values of the destination phone number corresponding to a particular DLR (in Delivered state). | Field | Sized Octets | Type | Description | | --- | --- | --- | --- | | Tag | 2 | Integer | Equal to **0x1503** (5379 Decimal) | | Length | 2 | Integer | Equal to **0x0002** (2 Decimal) | | Value | 3 | Octet String | **mcc** value as string. 3 characters | | Field | Sized Octets | Type | Description | | --- | --- | --- | --- | | Tag | 2 | Integer | Equal to **0x1504** (5380 Decimal) | | Length | 2 | Integer | Equal to **0x0002** (2 Decimal) | | Value | 1-3 | Octet String | **mnc** value as string. 1 - 3 characters | --- ## SMS Analytics ## Dashboard The dashboard will contain the information about the SMS that you have sent using 8x8. Below is a view of the entire dashboard page, in the following sections we will break down each section's contents. ![image](../images/a4f73c0-image.png) ### Statistics The Dashboard for SMS will show you the following statistics over the given time period which is set to the current week. | Column | Description | | :-------------------- | :--------------------------------------------------------------- | | Total SMS | The total amount of SMS sent in the given time period. | | Delivery Rate | Percentage of SMS delivered in the given time period. | | Total Cost | How much the SMS cost during this time period. | | Destination Countries | The number of different countries the SMS messages were sent to. | Below is a screenshot of the statistics section in the Dashboard. ![image](../images/6f0a45a-image.png) ### Charts There will be the following 3 charts available: - **SMS:** Shows the amount of SMS sent across the given time period for each of the days ![image](../images/798894f-image.png) - **Delivery Rate:** Shows how the delivery rate changes across the given time period for each of the days ![image](../images/c2d578f-image.png) - **Cost:** Shows how the average cost of the SMS changes on a given day ![image](../images/82e3240-image.png) ### Destination Countries This section will break down the amount of SMS sent to each country as well as the SMS operator responsible for sending the message. ![image](../images/252ab64-image.png) ### Campaigns This area will show the amount of SMS campaigns sent and a link to view the individual details of each campaign. ![image](../images/cf721b7-image.png) Clicking on the campaigns link will take you to the Campaigns tab of the Dashboard which will contain information about individual campaigns sent. ![image](../images/1850087-image.png) ## Reports The Reports page for SMS will show a further breakdown as compared to the dashboard and allow you to further filter the data. You will also be able to export the information as CSV file from the page to import the data into your own analytics systems. Besides containing statistics for messages sent via the SMS API, it also contains information relevant for the **Verification API, SMS Engage** and **Short URLs**. ### SMS Messages The SMS Messages section will allow you to filter your SMS sent by the following options | Column | Description | | :--------- | :--------------------------------------------------------------------- | | Subaccount | Filter by the 8x8 Subaccount used | | Country | Filter by the Country the SMS is delivered to. | | Operator | Filter by the SMS Operator of the Handset that the SMS is delivered to | | Date Range | Filter by the date range the SMS was sent. | ![image](../images/fd68aae-image.png) #### Daily Report It will also feature a **Daily Report** Section where it will have the following stats about messages. ![image](../images/809a56f-image.png) Here is a list of the columns in the **Daily Report** and their meaning. | Column | Description | | --- | --- | | Total | Total amount of messages sent/received in the time period. | | Sent | Total amount of messages sent in the time period. | | Total Chargeable | Total messages charged by 8x8 in the time period. | | Delivered | This means the message has been "delivered to the handset". If the status is not available from the operators, this means that 8x8 has received the confirmation from the carrier that the message has been "delivered to the carrier". | | Undelivered | We have received confirmation that the message was not delivered. This can be due to various reasons such as:1. Mobile handset is unavailable (e.g. mobile is switched off or on roaming mode)2. Filtered out by the operator | | Rejected | The message has not been accepted by our platform. This can be due to some errors such as incorrect mobile numbers or insufficient credit. You will not be charged for rejected messages. | | Received | The message has been received by our platform and it is currently being processed before being sent to the carrier. | | Delivery Rate | Percentage of SMS messages with a status of delivered. | | Cost | Cost of Each Message. | #### Export You can choose to export the daily report immediately which will send a URL link to the specified email. ![image](../images/6fc0e2a-image.png) There is also the option to schedule an export to be sent periodically to the specified email. ![image](../images/120bef8-image.png) ### SMS Conversions This reports page allows you to see conversions relevant to the [Verification API](/connect/docs/verification-api-get-started), specifically for SMS sent using the service. The main graph will show how many SMS have been sent using the service over time and what percentage have been successfully converted. This is useful for OTP use cases to track if there are any OTP conversion issues for example. ![image](../images/e875269-image.png) There is also a more detailed reports section on the same page which will show a detailed breakdown of the status of each verification attempt, conversion rate and cost. You can also export the report as a CSV file as well. ![image](../images/e40a2bd-image.png) ### SMS Engage This section will contain statistics on SMS Engage Surveys sent. Please see the [section](tutorial-sms-engage) on SMS Engage for more details. ### Short URLs 8x8 offers a URL Shortening service that can redirect from your chosen domain to our URL shortening domain. This feature allows us to offer click tracking. These Short URLs can be sent as part of SMS API messages or SMS Engage Surveys that you send with 8x8. The statistics for click tracking can been see in this section of the Reports. ![image](../images/651c46a-image.png) For further details on how to set up Short URLs, reach out to your Account Manager. ## Logs ### SMS Message List Below is the SMS logs in the Connect Dashboard which shows a record of the SMS that were sent and received by this account. ![image](../images/19e01a8-image.png) The SMS Logs above shown shares information about individual SMS messages sent including: | Column | Description | | :----------------- | :---------------------------------------------------------------------------------------------------- | | Subaccount | 8x8 subaccount used to send/receive the SMS message. | | Date sent/received | When the SMS was sent/received. | | Destination | Virtual Number or Mobile Number that received the message. | | Source | Sender ID or Mobile Number used to send the message | | Cost | Cost of sending the message. | | Status | Shows whether the SMS was sent successfully or other potential statuses including unsuccessful sends. | ### Individual SMS Record Inside each SMS record, the following information is available: ![image](../images/96d4733-image.png) This table shows a description of each of the fields in the SMS record. | Column | Description | | :-------------------------------------------- | :--------------------------------------------------------------------------------------------------------- | | Message ID | Uniquely identifies the message in the system. | | Country | The country the message was delivered. | | Operator | SMS Operator handling the message. | | Cost | Same as above, cost of sending the message. | | Date sent | Same as above, the date the message was sent. | | Client Batch ID | An optional parameter that can be used in an API call to identify a message with a unique string. | | Client Message ID | An optional parameter that can be used in an API call to identify a batch of message with a unique string. | | From | Same as Source, Sender ID or Mobile Number used to send the message. | | Message | The message body/content. | | To | Same as Destination, Virtual Number or Mobile Number that received the message. | | Source (Different from Source in Main Column) | Method the message was sent. Most commonly this is the HTTP API meaning API request to our SMS API. | | Encoding Type | The SMS encoding usually depends on the characters sent as part of the SMS. Either GSM7 or UCS2. | --- ## SMS Engage response webhook `POST` requests are sent by the 8x8 platform in `JSON` format to the webhook URL configured for your account. > 📘 > > You can configure a default webhook URL for your account by [contacting 8x8 support team](mailto:cpaas-support@8x8.com). > > ## Webhook Validity Period If we do not receive a response for the SMS engage survey promptly, our platform will continue checking for up to 48 hours. If the response is recorded after 48 hours, there will be no SMS engage response webhook sent. ## Webhook format Request body description | Property name | Property type | Description | | --- | --- | --- | | umid | uuid | Unique identifier generated by 8x8 for the message | | surveyId | string | unique name or identifier created by 8x8 for the SMS Engage | | surveyStartedAt | string | The date and time when the SMS Engage has been created in [ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601): `yyyy-MM-ddTHH:mm:ss.ffZ` | | surveySubmittedAt | string | The date and time when the client has completed and submitted the SMS Engage.[ISO 8601 format](https://en.wikipedia.org/wiki/ISO_8601): `yyyy-MM-ddTHH:mm:ss.ffZ` | | templateVariables | array of objects | Names and values of the variables used to send the SMS Engage | | answers | array of objects | Answers or responses submitted by the client based on the corresponding questions. | ### Template variable object | Property name | Property type | Description | | :------------ | :------------ | :---------------------- | | name | string | Template variable name | | value | string | Template variable value | ### Answer object | Property name | Property type | Description | | :------------ | :------------ | :------------------ | | questionId | string | Question identifier | | answerId | string | Answer identifier | | question | string | Question text | | answer | string | Answer text | ## Sample of SMS Engage webhook ```json { "umid": "84e7ee9a-5c41-e811-814c-020897df5459", "surveyId": "test-surveyid", "surveyStartedAt": "2018-04-16T09:59:48Z", "surveySubmittedAt": "2018-04-16T10:00:14Z", "templateVariables": [ { "name": "firstname", "value": "John" }, { "name": "order_nr", "value": "1010101" } ], "answers": [ { "questionId": "3", "answerId": "10003", "question": "Dear [url('FirstName')],\nOrder [url('order_nr')] was found as a duplication, please confirm below or your order will be cancelled", "answer": "Cancel Now" }, { "questionId": "16", "answerId": null, "question": "Full Name", "answer": "John" }, { "questionId": "4", "answerId": "10005", "question": "Do you confirm your response?", "answer": "Yes" } ] } ``` > 📘 > > The number of questions and their types will depend on the survey you have in place for your SMS Engage campaign. --- ## Submitted Sender ID Registrations **Sender ID Registration Submission and Sender ID Dashboard** * User will see the sender ID as under review which means that our back-end system is reviewing your Sender ID registration request and if required, manual checks may be carried out that may lengthen the review process ![Linked View of Submitted Sender ID Registrations](../images/00a34df43026cf87e59795071b3d2c0ced5e246f11c26b92d660ed07dedd47eb-unnamed_11.png)Linked View of Submitted Sender ID Registrations ![Unlinked View of submitted Sender ID registrations](../images/bc67bbdc54d41dafdf761dac0452d5cf0855596d2aba6f6654be35e7b88e0af0-unnamed_12.png)Unlinked View of submitted Sender ID registrations **Support Ticket** * Email Notification that Sender ID registration has been submitted and a ticket will be assigned ![image](../images/61247d3ec43d686b22228958974aa9880525459532c877970d13a8d3c74b39d6-unnamed_13.png) * Alternatively you can view the ticket via our support portal on 8x8 connect, ![image](../images/8a05452948dfb06b4d414f76d9911cbc6b88de9b74af835314e663a20a244609-Ticketconvo.png) --- ## Supported Messaging Apps Content Types This page describes the supported messaging apps content types, along with their character limits ## Supported Content Types by Channel | Channel | Channel type value | Text | Template | Image | Video | Button | File | Location | Interactive Messages | Rich Card | Rich Card Carousel | |:---------------------------| :----------------- | :--- | :------- | :---- | :---- | :----- | :--- | :------- | :------------------- | :-------- | :----------------- | | SMS | `sms` | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | | WhatsApp | `whatsapp` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | | Viber | `viber` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | | Zalo Notification Service | `ZaloNotification` | ✅ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | | LINE Official Notification | `LineNotification` | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | | Line Official Account | `line` | ✅ | ❌ | ✅ | ✅ | ❌ | ❌ | ✅ | ❌ | ❌ | ❌ | | RCS | `RCS` | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | *** ## Whatsapp - Whatsapp requires templates to be approved before they can be sent as a business-initiated message. However, for replies to customer-initiated conversations, templates are not mandatory and freeform text is supported. - Supported **File** formats are: | File Type | File Formats | Max File Size | |:----------|:--------------------------------------------------|:--------------| | Document | .pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .txt | 100 MB | | Image | .jpeg, .png | 5 MB | | Gif | .gif | 4 MB | | Audio | .aac, .m4a, .amr, .mp3, .ogg, .opus | 16 MB | | Video | .mp4, .3gp | 16 MB | #### Character Limit | Component | Character Limit | | :--------------------------------------------------------------- | :-------------- | | Text Message | 4,096 | | Interactive Message Button Title | 20 | | Interactive Message List Button | 20 | | Interactive Message List Row Title | 24 | | Interactive Message List Row Description | 72 | | Interactive Message List Section Title | 24 | | Template Header (text type) | 60 | | Template Body (with other components) | 1,024 | | Footer (various message types, for example opt-out instructions) | 60 | | Media Message Image Caption | 1,024 | | Flows Message CTA | 20 | *** ## RCS Business Messaging Supported **File** formats are: | Category | Extensions / MIME types | Notes | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------- | | **Images** | `.jpeg` / `.jpg` (`image/jpeg`), `.png` (`image/png`), `.gif` (`image/gif`) | Supported in rich cards & media messages | | **Video** | `.h263` (`video/h263`), `.m4v` (`video/m4v`), `.mp4` (`video/mp4`, `video/mpeg4`), `.mpeg` (`video/mpeg`), `.webm` (`video/webm`) | Supported in rich cards & media messages | | **Audio** | `.aac` (`audio/aac`), `.mp3` (`audio/mp3`, `audio/mpeg`, `audio/mpg`), `.mp4` (`audio/mp4`, `audio/mp4-latm`), `.3gp` (`audio/3gpp`), `.ogx` / `.ogg` (`application/ogg`, `audio/ogg`) | Media messages only | | **Documents** | `.pdf` (`application/pdf`) | Media messages (not rich cards) | | **File size cap** | Up to **100 MB** per attachment | | #### Limits | Message element / field | Limit | | ---------------------------------- | ------------------------------------ | | **Plain text message** | 3 072 characters | | **Rich-card title** | 200 characters | | **Rich-card description** | 2 000 characters | | **Suggested-reply text** | 25 characters | | **Suggested-action text** | 25 characters | | **Suggestion chips per message** | Up to 11 chips (4 in-card + 7 extra) | | **Carousel cards per message** | Up to 10 cards | | **Text caption with media** | 2 000 characters | | **Postback data** (per suggestion) | 2 048 characters | | **Rich-card payload size** | 250 KB | *** ## Viber - Viber templates are only available in Russia, Ukraine, and Belarus. - Supported **File** formats are: | File Type | File Formats | Max File Size | | :---------- | :------------------------------------------------------------ | :------------ | | Image | .png, .jpg, .gif | 1 MB | | Video | .mp4 (recommended), .3gp | 200 MB | | PDF | .pdf, .xps, .pdax, .eps | 200 MB | | Documents | .doc, .docx, .rtf, .dot, .dotx, .odt ,odf, .fodt, .txt, .info | 200 MB | | Spreadsheet | .xls, .xlsx, .ods, .fods, .csv, .xlsm, .xltx | 200 MB | #### Character Limit | Component | Character Limit | | :----------- | :-------------- | | Text Body | 1,000 | | Button Label | 30 | *** ## Zalo Notification Service - Zalo Notification Service (ZNS) is strictly for one-way messaging and requires templates to be approved before they can be sent. - ZNS template's character limit is 400 characters *** ## Line - Line corresponds to a LINE Official Account with the Messaging API enabled. - Supports bidirectional messaging (send and receive). - Supported outbound content types: Text, Image, Video, Audio, Location. - Supported inbound content types: Text, Image, Video, Audio, File, Location. *** ## LINE Official Notification - LINE Official Notification (LON) is strictly for one-way messaging and requires templates to be approved before they can be sent. - LON template's character limit is 500 characters --- ## SMS Template Management Templates is a feature within 8x8's Platform that will allow you to create message templates for later use, this page specifically covers SMS templates, although there are separate templates for our Messaging Apps functionality. Templates can have simple pre-filled messages beforehand or complex messages with parameters that can be customized to every message recipient. ## Video Guide We have created a video guide below to accompany this documentation page which will take you through creating SMS Templates. ## Creating Templates To create a template, go to the Templates page of the Connect Dashboard. ![Templates is located in the 8x8 Connect Dashboard](../images/6211dbb-image.png)Templates is located in the 8x8 Connect Dashboard Afterwards click **Create new template** which will pop up a dialog box. ![image](../images/2b2ed78-image.png) **Name:** A template name which you can use to search for and identify the template. **Sender ID:** Choose an SMS Sender ID to associate the template to. **Message:** Message Body of the template. This can either be a static message body or a variable message body. ### Message body types Message body types can either be **static** or **dynamic**. This is a **static** message type with a fixed message that cannot be modified or changed. ![Static Message Body](../images/bca3573-image.png) This is a **dynamic** message type with a parameters *`{{FirstName}}`* and *`{{date}}`* that are designed to be overwritten with actual values when sent through 8x8. ![Dynamic Message Body](../images/fe532b8-image.png) How these are sent are covered in the Multi-Channel Sender Section. ## Deleting Templates To delete a template, you can find a template in the list and then click the **Trash** icon, this will bring up a confirmation dialog to delete the template. ![image](../images/80d58c8-Screenshot_2023-10-10_at_1.45.10_PM.png) ![Delete Template Dialog](../images/76866d9-image.png "Delete Template Dialog") ## Editing Templates To edit existing templates, first find your template in the list either by the search functionality or by manually scrolling. When you have found your template you can click the Pencil Icon to edit and make any changes. ![image](../images/ccf666f-Screenshot_2023-10-10_at_1.48.28_PM.png) ![Edit Template Dialog](../images/bfeca98-image.png "Edit Template Dialog") --- ## Templates This section will cover how to manage templates within the 8x8 Connect Dashboard. There are primarily two types of templates: * **SMS Templates:** This covers SMS templates which can be used within the Multichannel Sender features in 8x8 Connect. * **Messaging Apps Templates:** This covers WhatsApp Templates on the Dashboard for now, although other Messaging Apps channels may be added in the future. Please see the corresponding pages in this section for further information on each type of template. --- ## Time zone & Onboarding > 🚧 **[BETA]** > > This product is currently in early access. Please reach out to your account manager to get more information. > ## Time Zones Certain date and time functions (in scripting) requires you to specify the timezone using a timezone id. You can get the supported time zone ids using the following request. You can use the optional parameter **contains** to filter timezones whose name contains the specified value. ```bash curl --location --request GET 'https://automation.8x8.com/api/v1/accounts/:accountId/steps/timezones?contains=europe' \ --header 'Authorization: Bearer {apiKey}' ``` If the request is successful, you will get the HTTP status code 200 with the following response body: ```json [ { "id": "Central Europe Standard Time", "name": "Belgrade, Bratislava, Budapest, Ljubljana, Prague (UTC+01:00)" }, { "id": "Central European Standard Time", "name": "Sarajevo, Skopje, Warsaw, Zagreb (UTC+01:00)" }, { "id": "E. Europe Standard Time", "name": "Chisinau (UTC+02:00)" }, { "id": "W. Europe Standard Time", "name": "Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna (UTC+01:00)" } ] ``` ## Onboarding To get access to this service, you will need your CPaaS account to be enabled for the Automation API. Please reach out to your account manager to get this done. For the events to be sent to the Automation service, you **don't** need to change your [webhooks](/connect/docs/webhooks-configuration-api). We will enable the Customer Integration on your account, for the events to be sent to the Automation service. You can still continue to manage your [webhooks](/connect/docs/webhooks-configuration-api) as usual and you will still receive the events. --- ## Token example creation The Token Creation API can be used to create a token or to create a room, here are few examples: **Sample 1 - Token creation:** ```bash curl -X POST https://video-agent.8x8.com/api/v1/tokens \ -H "Content-Type: application/json" \ -H 'authorization: Bearer YourAPIKey' \ ``` **Sample Response 1:** ```json { "auth_token": "[...]eyJiJIUzI1NiME5B87.qPWXyNNDHBx_LftaH" } ``` In the sample 1, you are only getting a token and not creating a room. This will allow you to login agents later on and to use the agent console as per usual. **Sample 2 - Room Creation:** ```bash curl -X POST https://video-agent.8x8.com/api/v1/tokens \ -H "Content-Type: application/json" \ -H 'authorization: Bearer YourAPIKey' \ -d '{"create_room": true, "phone_number":"+6590893208", "call_reference":"123abcde"}' ``` **"create_room"** is an optional parameter, default is false. If you want to create a room while getting the token, you need to set this parameter to ‘true’. **"phone_number"** is an optional parameter, it should be an international phone number. **"call_reference"** is an optional parameter, it should be a string of min 8 characters and max 20 characters. **Sample Response 2:** ```json { "call_reference": "123abcde", "guest_link": "https://www.video-interaction.com/guest/Y56IiyJMxx", "auth_token": "[...]eyJiJIUzI1NiME5B87.qPWXyNNDHBx_LftaHa" } ``` In the sample 2, you are getting a token and creating a room. This will allow you to login the agents later on and to have a room already created after login. If you provide a phone number, 8x8 will send the link via SMS to the phone number, when an agent logs with the token. If you want to send the link yourself, do not provide a phone number (you can still provide a Call Reference). --- ## Steps ## Overview **Steps** make up a **workflow definition**. Each step has a unique id that identifies it within that workflow definition. This page aims to define each type of step and the properties that make up that step to better understand how they can be used as part of a workflow definition. Steps allow you to create custom workflow definitions that can perform different actions within that workflow instance. ## List of Steps | Step Name | Description | Example | | --- | --- | --- | | SMS Message | SMS step allows you to send a message to a recipient as a SMS. | Send an SMS message to a customer. | | ChatApps Message | ChatApps Message step allows you to send a message using 8x8 Chat Apps messaging API. | Send a ChatApps message to a customer. | | Wait | Wait step allows you to make the workflow wait for a specified period of time before executing the next step. | Wait 2 hours to send a reminder message to a customer. | | Branch | Allows you to create multiple paths in a workflow. | Use a Branch step to decide what message to send based on a customer's reply. If a customer replies with 1, send a reminder message. If they reply with 2, send a different message. | | WaitForReply | The WaitForReply step type allows your workflow to pause till you receive a reply from an end user | Wait till a customer responds to a message. You can pair it with a branch step to decide what to do based on that response.For example you may want to send a message asking a user to check their email for an OTP, then use Wait for Reply to wait for the user to input the OTP before proceeding to the next step. | | Voice Message | Send a Voice Call to a number | Play a voice message to a specified phone number with a reminder. | | HTTP Request | Send an HTTP request to an endpoint. Useful for integrating with APIs. | This may be useful to query an HTTP endpoint and use the value returned as part of the flow. For example you can query the Google Calendar API for available meeting slots and use the slots as part of a message response for a user. | | Jump To | Skip to another step in the workflow definition. This is often useful to better organise your workflow definition. | For example there may be a menu with many options and some of them refer to the same message such as:1) Office Hours of Restaurant Location 12) Office Hours of Restaurant Location 23) Office Hours of Restaurant Location 3If 1+2 are the same, you can use a Jump Step to point to a message that would contain the same message. | | Send to Converse | Send the current conversation to [Converse](/converse) for an agent to take over. | As part of a menu, you can give the option for the user to escalate to a live agent. | ## Step Descriptions ### SMS Message SMS step allows you to send a message to a recipient as a SMS using the 8x8 SMS API. ![Example of Step](../images/31e3b54-image.png) ![Example of Step Properties](../images/0eccbeb-image.png) | Property | Description | Example Value | | --- | --- | --- | | Step Name | Unique name that identifies a step. | send_sms123 | | Subaccount | The 8x8 Subaccount that the message will be sent from. | subaccount123 | | Sender ID | Either the SMS Sender ID or the Virtual Number that the message will be sent from and the user will see. | 44703492340 (Number that is tied to the subaccount123) | | Destination | The Phone Number that you would like to send the SMS to. | `{{data.payload.source}}` (If the workflow was triggered by an inbound message, this variable would refer to the sender of that inbound message). | ### ChatApps Message ChatAppsMessage step allows you to send a message using 8x8 Chat Apps API. ![Example of Step](../images/258c034-image.png) ![image](../images/63f27cb-image.png) ![Example of Step Properties](../images/4bd5833-image.png) | Property | Description | Example Value | | --- | --- | --- | | Step Name | Unique name that identifies a step. | send_sms123 | | Subaccount | The 8x8 Subaccount that the message will be sent from. | subaccount123 | | Destination | The Phone Number that you would like to send the SMS to. | `{{data.payload.source}}` (If the workflow was triggered by an inbound message, this variable would refer to the sender of that inbound message). | | Select an exisiting template or compose a new message | If required, you should select an existing template for the ChatApps Platform you are using. For example WhatsApp requires templates for the first message sent to a customer. | N/A | | Message | A Preview of the message that will be sent to a customer. | Hi `{{1}}`, good news, your package has been shipped! | | Parameters | Variables that can be inputted into a message template that will replace the placeholders. | If the variable is `{{1}}` and the value is "Hello", `{{1}}` will be replaced by "Bob".**Example Template:**Hi `{{1}}`, good news, your package has been shipped!**Replaced Message:**Hi Bob, good news, your package has been shipped! | ### Wait Wait step allows you to make the workflow wait for a specified period of time before executing the next step. ![Step and Properties in UI (Note: The duration is yet to be updated, that's why it shows 00:00:00 on the left)](../images/307f3e8-image.png) (Note: The duration is yet to be updated, that's why it shows 00:00:00 on the left) | Property | Description | Example Value | | --- | --- | --- | | Step Name | Unique name that identifies a step. | wait_6686 | | Duration | Duration to wait before proceeding onto the next step | 5 hours | ### Branch Allows you to create multiple paths in a workflow. The example below shows the next steps after and before the branch to illustrate it's use. * The Wait For Reply Step (waitforreply_8622) just before this is waiting for a input from a user * The Branch Step (branch_8338) checks the value returned in the Wait For Reply Step * If 1, chatappsmessage_0501 step is executed, delivering the message "Please enter your email address:" * If 2, chatappmessage_9836 step is executed, delivering the message "Goodbye". ![image](../images/d68b3c1-image.png) ![Example of Step Properties](../images/f66d1d4-image.png) | Property | Description | Example Value | | --- | --- | --- | | Step Name | Unique name that identifies a step. | branch_8338 | | Please select a condition | There are a few possibilities of values to check such as:1) Check if a value contains a string2) Check country code of a mobile number3) Check if date falls within some time of day4) Check the day of the week of a date5) Custom Condition | Ii you select "Check if a value contains a string" that allows you to branch off of a user input provided in a previous step. | | Value to check | This allows you to refer to a previous step in the workflow definition and the value it contains. | Use the value a user inputted in a Wait For Reply Step as part of the branch logic | | Keywords to Compare | Fixed values to check the property "Value to Check" against. | You can add 1, 2, 3 as values to compare to create a menu such as the following:1) Enter 1 to reserve a table2) Enter 2 for restaurant hours3) Enter 3 for location | ### Wait For Reply The WaitForReply step type allows your workflow to pause till you receive a reply from an end user. For example you may want to send a message asking a user to check their email for an OTP, then use Wait for Reply to wait for the user to input the OTP before proceeding to the next step. ![Example of Step](../images/1538630-image.png) ![image](../images/12f94c6-image.png) ![Example of Step Properties](../images/cb17e53-image.png) | Property | Description | Example | | --- | --- | --- | | Step Name | Unique name that identifies a step. | waitforreply_8622 | | From | Define which phone number or channel source to wait for a reply from. | `{{data.payload.user.channelUserId}}` | ### Voice Message The Voice message has 3 possible actions which will change the properties available. There are also generic properties that are required by all 3 possible actions. #### Voice Message Step and Generic Properties Image ![Example of Step Example of Step Properties for](../images/b9d5d26-image.png)Example of Steps ![Example of required properties for all actions](../images/2f29d51-image.png) for all actions #### Generic Properties required for all actions | Property | Description | Example Value | | --- | --- | --- | | Step Name | Unique name that identifies a step. | voicemessage_5246 | | Please Select a Voice Subaccount | Choose the subaccount to use for the voice call. It should have a 8x8 Phone Number configured | subaccount123 | | Action | Choose what type of voice message to play to a user:1) Message with DTMF2) Text to Speech3) Play an Audio File | "Message with DTMF" | | Source | The Phone Number to place the call from | +6512345678 | | Destination | The Phone Number to call | A Phone Number such as +6512345678. Or a variable such as `{{data.PhoneNumber}}` if the workflow is triggered by a HTTP trigger for exmaple. | #### "Text to Speech" Action Properties Image ![Example of Step Propertie](../images/5cf1f95-image.png) #### "Text to Speech" Action-specific Properties | Property | Description | Example Value | | --- | --- | --- | | Language | The Language of the message | en-US for English (US) | | Voice Profile | A customized and unique synthesis of vocal characteristics, pitch, and intonation created for a specific user, providing a personalized and natural-sounding audio experience. | en-US en-US-AriaRUS for a Female English speaker | | Speed | How fast to playback the message | 1 for default speed, 2 for 200% speed. | | Repetition | How many times to play the message | 1 for playing the message once. | #### "Play an Audio File" Action Image ![Example of Properties for Play an Audio File](../images/4676b33-image.png) #### "Play an Audio File" Action Properties | Property | Description | Example Value | | --- | --- | --- | | Repetition | How many times to play the message | "1" to play the message once | | Audio URL | Publicly accessible URL of the audio file to play | [https://filestorage.com/file1.mp3](https://filestorage.com/file1.mp3) | #### "Message with DTMF" Properties Image ![Example of Message with DTMF Properties](../images/6c2efd1-image.png) #### "Message with DTMF" Action Properties | Property | Description | Example Value | | --- | --- | --- | | Message | The message to play using text to speech | "Hello, this is ABC Restaurants" | | Language | The Language of the message | en-US for English (US) | | Voice Profile | A customized and unique synthesis of vocal characteristics, pitch, and intonation created for a specific user, providing a personalized and natural-sounding audio experience. | en-US en-US-AriaRUSfor a Female English speaker | | Speed | How fast to playback the message | "1" for default speed, "2" for 200% speed. | | Repetition | How many times to play the message | "1" for playing the message once. | | Minimum Digit | Minimum amount of digits of input to accept | "1" for needing to accept 1 digit of input | | Maximum Digit | Maximum amount of digits of input to accept | "2", so it will only accept at maximum 2 digits | | DTMF Timeout (ms) | How long in milliseconds to wait for a DTMF response | "5000" would make the step wait for 5 seconds / 5000 milliseconds | | Call Timeout (ms) | How long in milliseconds to wait for an answer to a call | "5000" for making the step wait 5 seconds for an answered call. | | Complete on Hash | Whether to complete the message on a hash | "Yes" to require a hash to input a DTMF, "No" to accept DTMF input with just the digit | | Number of Tries | How many times to try to collect input | "2" for making the step try twice to collect a DTMF | | End Message | Message to play after collecting a DTMF input | "Thank you for your response" to play that message at the end of DTMF collection. | | DTMF Inputs | Branching paths based on DTMF input. You can create multiple inputs for as many paths as you would like to support. | Paths:1 for office hours2 for office location | ### HTTP Request Send an HTTP request to an endpoint. Useful for integrating with APIs. ![Example of Step](../images/b6a4526-image.png) ![Example of Step Properties](../images/cee058e-image.png) ![Example of "Output" Property](../images/040240f-image.png) ![Example of Query Preoperty](../images/04c4c20-image.png) | Property | Description | Example Value | | --- | --- | --- | | Step Name | Unique name that identifies a step | httprequest_1255 | | URL | URL and Method to send an HTTP request to | [https://sheets.googleapi.com/v4/spreadsheets](https://sheets.googleapi.com/v4/spreadsheets) and GET to send a GET request to that URL | | Header | HTTP Headers to attach to the request | "content-type" and "application/json" to send this key value pair as a part of the HTTP header in the request. | | Query | HTTP Query to attach to the request. | "userInput" and "1" to send this key value pair as part of the HTTP request. | | Output | Save outputs from the response of the HTTP request for use in either this step or subsequent steps. | "status" and "step.responseCode" to save the status property for use later in the workflow. It can be referred as a variable by using `{{data.status}}`.You can use this value to take a different action depending on the response code in a branch step for example. | | Request Body | The request body to send as part of the request. The format should match the Content-Type header that you supply as part of the request. | For example if the endpoint you are sending expects JSON, you should specific the content type appropriately in the header and then send JSON in the request body such as:{"values": [["`{{data.PhoneNumber}}`","`{{data.Name}}`"]]} | | Timeout | Timeout for the HTTP request | "30 seconds" to wait 30 seconds before timing out | ### Jump To Skip to another step in the workflow definition. This is often useful to better organise your workflow definition. ![image](../images/519109a-image.png) | Property | Description | Example value | | --- | --- | --- | | Select a step | This should refer to a step within the same workflow definition that you would like to execute after this jump step. | "chatappsmessage_6359" | ### Send to Converse Send the current conversation to [Converse](/converse) for an agent to take over. ![image](../images/df2ca45-image.png) | Property | Description | Example Value | | --- | --- | --- | | Step Name | Unique name that identifies a step | "sendtoconverse_6641" | --- ## Triggers & Steps > 🚧 **[BETA]** > > This product is currently in early access. Please reach out to your account manager to get more information. > > ## Triggers Here are the different triggers we support, at the moment: **inbound_sms** : meaning when you receive a new incoming SMS on the specified subaccount. **inbound_chat_apps** : meaning when you receive a new incoming ChatApps message on the specified subaccount. **http_request** : generic trigger that can start a workflow via HTTP events like webhooks with any kind of payload (application/json). This is great if you want to trigger workflows from external systems. Example HTTP request to start your workflow: ```bash curl --location -X POST 'https://automation.8x8.com/api/v1/accounts/:your_account_id/triggers/http_request?subAccountId=:your_subaccount_id' \' \ --header 'Content-Type: application/json' \ --data-raw '{ "f1": "value1", "f2": 123456, "f3": false, "o1": { "nf1": "value1", "nf2": "value2" } }' ``` One workflow definition can only have one trigger, defining a trigger is mandatory. You can create similar workflows with different triggers, if needed. ## Steps Workflow definitions are collections of workflow steps. Every step must have an id and a step type. The id of the step must be unique within the workflow definition. Steps may have properties that can be set when you are creating a workflow definition (e.g. set a HTTP header for a HTTP request) or at runtime by the workflow using outputs (e.g. HTTP response code of a HTTP request). **Here are the different Steps we support:** * **SMS** * **ChatAppsMessage** * **HttpRequest** * **Wait** * **If** * **WaitForReply** * **VoiceMessage** * **WaitForDTMF** More details on each step: **Send an SMS** : *SMS* step allows you to send a message to a recipient as a SMS. ```json { "id": "step1", "stepType": "SMS", "inputs": { "subAccountId": "acme_corp_sms", "destination": "+6512345678", "text": "Hello, world!" }, "outputs": { "smsRequestId": "{{step.requestId}}", "smsUmid": "{{step.umid}}", "smsStatus": "{{step.status}}", "smsDescription": "{{step.description}}" }, "nextStepId": "step2" } ``` | Property | Description | Type | |------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------| | id | Unique id of the step. | string | | stepType | Step type. | string | | inputs | Input parameters for SMS request: - **subAccountId**: Sub account id to send the message from. - **source**: Source number (senderId). - **destination**: MSISDN to send message to. - **text**: Message body. For information on all supported input options, refer to SMS documentation. | object | | outputs | Properties of the step to save to workflow execution context to use in other steps (Optional). Supported properties are: - **requestId**: Unique identifier of the HTTP request. - **umid**: Unique identifier of the message. - **status**: Status of the message request. - **description**: Descriptive message on the status of the message. For information on all supported statuses please refer to our SMS documentation. | object | | nextStepId | Step id of the next step to execute (Optional). If a next step id is not specified, workflow will terminate after this step. | string | **Send a Chat Apps message** : *ChatAppsMessage* step allows you to send a message using 8x8 Chat Apps messaging API. ```json { "id": "step1", "stepType": "ChatAppsMessage", "inputs": { "subAccountId": "acme_corp_chatapps", "user": { "msisdn": "+6512345678" }, "type": "text", "content": { "text": "Hello, World!", "sms": { "encoding": "AUTO", "source": "SENDERID" } } }, "outputs": { "requestId": "{{step.requestId}}", "requestUmid": "{{step.umid}}", "requestStatus": "{{step.status}}", "requestDescription": "{{step.description}}" }, "nextStepId": "step2" } ``` | Property | Description | Type | |------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------| | id | Unique id of the step. | string | | stepType | Step type. | string | | inputs | Chat Apps message request parameters: - **subAccountId**: Sub account id to send the message from. - **user**: Recipient information. - **type**: Type of message. - **content**: Message content. For information on all supported user, type and content models please refer to our Chat Apps documentation. | object | | outputs | Properties of the step to save to workflow execution context to use in other steps (Optional). Supported properties are: - **requestId**: Unique identifier of the HTTP request. - **umid**: Unique identifier of the message. - **status**: Status of the message request. - **description**: Descriptive message on the status of the message. For information on all supported statuses please refer to our Chat Apps documentation. | object | | nextStepId | Step id of the next step to execute (Optional). If a next step id is not specified, workflow will terminate after this step. | string | **Make a HTTP request** : *HttpRequest* step allows you to make a custom HTTP request and consume its response. For HTTP requests with request data, automation currently supports application/json content type. Automation service will evaluate the dynamic request data at runtime, serialise the input body to json before sending the request. ```json { "id": "step1", "stepType": "HttpRequest", "inputs": { "url": "https://sample.api.com/newrecord/", "method": "POST", "headers": { "Authorization": "Bearer 4ff3987hf934hf3895b469dc0" }, "body": { "property_1": 1, "property_2": "{{'umid: ' + data.umid}}", // dynamic field using javascript. "property_3": "{{data.receivedAt}}", // datetime field. "property_4": { "nested_property": "{{'msisdn: ' + data.source}}" } // Nested field. } }, "outputs": { "httpCode": "{{step.responseCode}}", "httpReasonPhrase": "{{step.reasonPhrase}}", "httpResponse": "{{step.responseBody}}" }, "nextStepId": "step2" } ``` | Property | Description | Type | |------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------| | id | Unique id of the step. | string | | stepType | Step type. | string | | inputs | Input parameters for make HTTP request: - **url**: Url with the path parameters. - **method**: HTTP method like GET, POST, PATCH, etc. - **headers**: HTTP headers as an object (Optional). - **parameters**: Query parameters as an object (Optional). - **body**: Request body. - **timeoutSeconds**: Request timeout in seconds (Optional). | object | | outputs | Properties of the step to save to workflow execution context to use in other steps (Optional). Supported properties are: - **responseCode**: HTTP status code. - **reasonPhrase**: HTTP reason phrase. - **responseBody**: HTTP response body. | object | | nextStepId | Step id of the next step to execute (Optional). If a next step id is not specified, workflow will terminate after this step. | string | **Set a delay** : Wait step allows you to make the workflow wait for a specified period of time before executing the next step. For example, you might have a workflow triggered by an incoming message received outside of office hours and you may want to make the workflow wait till office hours to send an automatic reply. ```json { "id": "step1", "stepType": "Wait", "inputs": { "duration": "0.00:01:00" }, "nextStepId": "step2" } ``` | Property | Description | Type | |------------|------------------------------------------------------------------------------------------------------------------------------------------------------|--------| | id | Unique id of the step. | string | | stepType | Step type. | string | | inputs | Wait step supports the following input parameters. - **duration**: time to wait before executing the next step (format: d.HH:mm:ss). | object | | nextStepId | Step id of the next step to execute (Optional). If a next step id is not specified, workflow will terminate after this step. | string | **If condition** : The **if** condition allows you to create multiple paths in a workflow. While you can use Branch step with just one branch to create an **if** condition, **If** step allows you to specify a conditional path more easily if you don't need complex branching. In the below example, the two new properties for **If** are **do** and **inputs.condition**. If the **inputs.condition** evaluates to **true**, all the steps specified inside do will be executed. ```json { "id": "step1", "stepType": "If", "inputs": { "condition": "{{!isTimeOfDayBetween(data.payload.status.timestamp, '08:00:00', '17:00:00', 'Singapore Standard Time')}}" }, "do": [ [ { "stepType": "HttpRequest", "id": "call_webhook", "nextStepId": "send_ca", "inputs": { "url": "http://localhost:8080/mock", "method": "GET" } }, { "stepType": "ChatAppsMessage", "id": "send_ca", "inputs": { "subAccountId": "acme_corp_chatapps", "user": { "msisdn": "{{data.payload.user.msisdn}}" }, "type": "text", "content": { "text": "Hello, world!" } } } ] ] } ``` | Property | Description | Type | |------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------| | id | Unique id of the step. | string | | stepType | Step type. | string | | inputs | If step supports the following input parameters: - **condition**: An expression that evaluates to **true** or **false**. | object | | do | Sequence of steps to be executed if the condition in inputs evaluates to true. | string | **Wait for Reply** : The WaitForReply step type allows your workflow to pause till you receive a reply from an end user. For example, you can create a workflow which sends a message to an end user, wait for their reply and act based on various scenarios based content of the reply, for example. If you have a workflow that that is waiting for a reply from user and the user replies, the automation service makes sure that the paused workflow resumes on their reply instead of starting new workflows of the same type. ```json { "stepType": "WaitForReply", "id": "step1", "inputs": { "from": "+6500000000", "channel": "whatsapp", "timeout": "00:05:00" }, "outputs": { "reply": "{{step.reply}}" }, "selectNextStep": { "success_step": "{{step.reply != null}}", "failure_step": "{{step.reply == null}}" } } ``` | Property | Description | Type | |------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------| | id | Unique id of the step. | string | | stepType | Step type. | string | | inputs | If step supports the following input parameters. - from: Sender of the inbound message (or an expression that evaluates the sender). If you are waiting for a reply on SMS channel, wait on msisdn (**data.payload.user.msisdn**). If you are waiting on a chat apps channel, wait on channel user id (**data.payload.user.channelUserId**) (exact path depends on the MO version you are on.) - channel: Channel on which the sender should be replying. E.g. sms, whatsapp, viber, etc. - timeout: Wait timeout. If the user does not reply within this timespan, workflow continues to next step. Timeout parameter is optional. If not set, a system level default (usually one day) is used. Accepts a string in the format of “d.HH:mm:ss“. If you omit the “HH:mm:ss“ part, timespan is interpreted as days. **Examples: 1.** “duration“: “1.6:30:15“ indicates 1 day, 6 hours, 30 minutes and 15 seconds. **Examples 2.** “duration“: “00:30:00“ indicates 30 minutes. **Examples 3.** “duration“: “5“ indicates 5 days. | object | | outputs | WaitForReply exposes the data in the reply in the in the property reply. You can save it to workflow context using step.reply and use it in a following step. | array | | selectNextStep | Step selector for various outcomes. | string | **Voice Message** : The VoiceMessage step type allows your workflow to send a Voice Message using 8x8 Voice API. For example, you can create a workflow which sends a voice message reminder to an end user. As per the 8x8 Voice API, you can choose from 3 types of voice messages: * say: use text to speech to read your message to the end users * say&capture: use text to speech to read your message and allows to capture DTMF tone from the end users * playFile: play a recorded file to the end users ```json { "id": "step1", "stepType": "VoiceMessage", "inputs": { "subaccountId": "acme_corp_voice", "clientRequestId": "myId123" "action": "say", "params": { "source": "+6500000000", "destination": "6500000000", "text": "Hello, world!", "repetition": 1, "voiceProfile": "en-US-ZiraRUS", "speed": 1 } }, "outputs": { "response": "{{step.response}}" }, "nextStepId": "step2" } ``` | Property | Description | Type | |------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------| | id | Unique id of the step. | string | | stepType | Step type. | string | | inputs | Inputs supported by Call step: - subAccountId: Voice enabled subaccount id - clientRequestId: Client message request id. - action: Call action. Allowed actions are say, say&capture, playFile. Refer to [voice API documentation](/connect/reference/send-single-1) for more information. - params: Call parameters. Call parameters supported by voice api as documented in [voice API](/connect/reference/send-single-1). | object | | outputs | Output properties supported by VoiceMessage step: - response: Response received from the voice API. Refer to voice [API documentation](/connect/reference/send-single-1) for the exact response. | object | | nextStepId | Step id of the next step to execute (Optional). If a next step id is not specified, workflow will terminate after this step. | string | **Wait For DTMF** : The WaitForDTMF step type allows your workflow to wait for a user input (DTMF tone) after a Voice message step using say&capture action. Hence the WaitForDTMF step should be preceded by a VoiceMessage step. Important thing to note about WaitForDTMF step is in how to choose the correlationId, which is what is used to correlate an inbound call with the wait for DTMF step. correlationId is chosen based on clientRequestId (if present) or the uid in the DTMF response. Recommendation is to set a unique (per DTMF request-response transaction) clientRequestId in the Call that requests the DTMF response. For instance, you can use the uuid() function to generate a uuid for the client request id and use it as the correlation id (which is recommended by the voice team) or you can save the uid of the call response to workflow context and use it as the correlation id. ```json { "id": "step1", "stepType": "WaitForDTMF", "inputs": { "dtmfRequestId": "{{data.clientRequestId}}", "timeout": "00:02:00" }, "outputs": { "dtmf": "{{step.dtmfData.actionDetails.dtmf}}" }, "selectNextStep": { "dtmf_1": "{{data.dtmf == '1'}}", "dtmf_2": "{{data.dtmf == '2'}}", "dtmf_3": "{{data.dtmf == '3'}}", "invalid_dtmf": "{{data.dtmf != null && data.dtmf != '1' && data.dtmf != '2' && data.dtmf != '3'}}", "no_reply": "{{data.dtmf == null}}" } } ``` | Property | Description | Type | |----------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------| | id | Unique id of the step. | string | | stepType | Step type. | string | | inputs | Inputs supported by WaitForDTMF step: - dtmfRequestId: A unique identifier to correlate the DTMF response with DTMF request. - timeout: Wait timeout. If the user does not reply within this timespan, workflow continues to next step. Timeout parameter is optional. If not set, a system level default (usually one day) is used. Accepts a string in the format of “d.HH:mm:ss“. If you omit the “HH:mm:ss“ part, timespan is interpreted as days. Examples: 1. “duration“: “1.6:30:15“ indicates 1 day, 6 hours, 30 minutes and 15 seconds. 2. “duration“: “00:30:00“ indicates 30 minutes. 3. “duration“: “5“ indicates 5 days. | object | | outputs | Output properties supported by WaitForDTMF step: - dtmfData: DTMF response received from the voice API. Refer to [voice API documentation](/connect/reference/send-single-1) for the exact response. | object | | selectNextStep | Step selector for various outcomes. (Alternatively, you can use nextStepId and point to a branch step and work from there.) | string | --- ## Triggers ## Overview **Triggers** are the initial entry point into your workflow definition. They define what event will start a workflow instance. For example, if you would like to start your workflow when the number tied to your subaccount receives an SMS, you can choose a "Inbound SMS" trigger. Further types of triggers are defined below. ## List of Triggers | Name | Description | | --- | --- | | Inbound SMS | Trigger the workflow when you receive a new incoming SMS on the specified subaccount. | | Inbound ChatApps | Trigger the workflow when you receive a new incoming ChatApps message on the specified subaccount | | HTTP Request (Webhooks) | Trigger the workflow when you receive a HTTP events like webhooks with any kind of payload (application/json). This is useful to trigger workflows from external systems. | ## Trigger Descriptions ### Inbound SMS Below is an example of a SMS trigger step setup as part of the workflow. It is the first part of a workflow and the next step is a branch. This means when this subaccount receives an SMS, it will send it to the branch step. This may be useful to use when you want to trigger an event such as sending a reply to an inbound SMS to that subaccount. ![Inbound SMS as part of a Workflow Definition](../images/63acb16-image.png) Inbound SMS Trigger as part of a Workflow Definition ![Inbound SMS Trigger Properties](../images/fddc703-image.png) Inbound SMS Trigger Properties | Property | Description | | --- | --- | | Please select a new subaccount | Select the subaccount that should trigger this workflow when it receives an inbound SMS | ### Inbound Chat Apps ![Inbound Chat Apps as part of a workflow](../images/9c9c941-image.png) Inbound Chat Apps Trigger as part of a workflow ![Inbound Chat Apps Properties](../images/6d2840d-image.png) Inbound Chat Apps Trigger Properties Similar to the Inbound SMS Trigger, this is triggered when a message is sent to the subaccount. However it is triggered off of a Chat Apps message such as an incoming WhatsApp/Viber message to a WhatsApp/Viber account tied to the subaccount. This may be useful to use when you want to trigger an event such as sending a reply to an inbound ChatApps message to that subaccount. | Property | Description | | --- | --- | | Please select a new subaccount | Select the subaccount that should trigger this workflow when it receives an inbound Chat apps message. | ### HTTP Request (Webhooks) ![HTTP Trigger as part of a Workflow](../images/0d426e5-image.png) HTTP Trigger as part of a Workflow ![HTTP Trigger Properties](../images/280dec0-image.png) HTTP Trigger Properties | Property | Description | Example | | --- | --- | --- | | Subaccount | The subaccount that should contain this URL endpoint | "subaccount123" | | Webhook Trigger | This property is not modifiable but it defines the URL endpoint your system should send an HTTP request to, in order to trigger the workflow | [https://automation.8x8.com/api/v1/accounts/InternalDemoCPaaS_8dD15/triggers/http_request?subAccountId=subaccount123](https://automation.8x8.com/api/v1/accounts/InternalDemoCPaaS_8dD15/triggers/http_request?subAccountId=subaccount123) | | Data | This is a list of parameters that can be accepted by this endpoint. They will later be accessible in the workflow as variables such as {{data.propertyName}} | If you define a "price" and "item" property in the trigger, then {{data.price}} and {{data.item}} will be variables in the workflow you can refer to for the values that were sent as part of the HTTP request to the webhook trigger.For example if a curl request is sent as follows:curl --location -X POST '[https://automation.8x8.com/api/v1/accounts/InternalDemoCPaaS_8dD15/triggers/http_request?subAccountId=subaccount123'](https://automation.8x8.com/api/v1/accounts/InternalDemoCPaaS_8dD15/triggers/http_request?subAccountId=subaccount123')--header 'Content-Type: application/json'--header 'Authorization: Bearer YOUR_API_KEY'--data-raw '{"price": "10","item": "vacuum"}'Then {{data.price}} will be 10 and {{data.item}} will be vacuum in the workflow instance that is called. | --- ## Troubleshooting(3) ## Overview Troubleshooting a workflow is currently done via the Automation Builder API. Specifically the API allows you to retrieve error logs related to a workflow instance. ## Steps to Troubleshoot These steps are assuming you would like to retrieve the most recently executed instance of a workflow to see the errors, although you can easily modify them to retrieve errors for a previous instance. ### 1. Get list of workflow instances Use the following [endpoint](/connect/reference/get-workflow-instances) to retrieve a list of workflow instances currently active and choose the most recently ran instance. When calling this endpoint, you will receive a list of the most recent instances. Copy the **workflowId** from the instance you would like to check errors for which will be used in the next step. ![image](../images/13b1124-image.png) ### 2. Retrieve errors from workflow instance Once you have the most recently executed instance's workflowId, use this value in this [endpoint](/connect/reference/get-instance-errors) to retrieve any errors associated with that workflowId. In this case, there is an error in the HTTP Step that tells us that it is preventing the workflow from being completed. ![image](../images/0e7a9e3-image.png) ### Common Errors This is a list of commonly encountered error messages and general recommendations on how the issue may be resolved. If you are unable to resolve the error, please send an email with the error details to [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) | Error Message Example (may not match text exactly) | Troubleshooting Recommendations | |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------| | Unexpected character encountered while parsing value: <. Path '', line 0, position 0 | This is usually encountered with the HTTP Step due to an invalid JSON object encountered at the endpoint that the HTTP Step is sending to. | | Failed to send chat apps message because Invalid 'msisdn'.. Error id: xxxxxxx-xxxxxx | Invalid phone number / msisdn provided in the Send Chat Message or SMS Message Step. | | Failed to send chat apps message because Empty 'user' | Invalid user object that was provided in the Send Chat Message or SMS Message Step. | | Call failed. The SSL connection could not be established, see inner exception: POST [https://chatapps.8x8.com/api/v1/subaccounts/subaccount_text_example/messages](https://chatapps.8x8.com/api/v1/subaccounts/subaccount_test_example/messages) | Internal error, please try to invoke the workflow again and report issue to support email above. | | Failed to evaluate the expression. Error: Object reference not set to an instance of an object. | Internal error, please try to invoke the workflow again and report issue to support email above. | --- ## Tutorial: Opt Out Keyword (SMS) ## Overview This tutorial will show you how to use Automation Builder to build an opt-out flow for SMS. ## Limitations ### Contacts API operates at the Account-level The **Contacts API** is designed to function at the **Account-level** and does NOT support operation at the **Subaccount** level. Contacts added to blacklist groups will be blacklisted across the **entire Account, affecting all Subaccounts** hence it is not recommended for use cases where you may need to manage separate opt out lists per subaccount. If you need opt-out management at the Subaccount level, you'll have to handle it separately using a third-party system. Please see [this](tutorial-opt-out-keyword-messaging-apps-whatsapp-for-third-party-system) related tutorial for ideas on how to integrate with third-party systems. ### Contacts API blacklist syncs hourly The Contacts API's blacklist sync occurs hourly. Consequently, customers added to a blacklist group may continue to receive messages until the sync process updates our system. ## Tutorial Steps 8x8 has already pre-built an Automation Builder template which allows you to add a keyword which will allow customers to opt out of further communications. We will use this template as the basis of the tutorial. There is setup work we need to do which is covered in the tutorial such as creating the opt-out group via Contacts and obtaining the opt-out group's ID via the Contact API. ### Create Opt-Out Group Visit the [Contacts](https://connect.8x8.com/messaging/contacts) page in Connect where we will create a new group. Once on this page below, click into "Contact Groups" and hit the red **Create Group** button. ![image](../images/2a52aeb-image.png) We should be seeing the screen below, you can fill in any values for the Group Name and Description, but ensure that "Blacklisted" is checked since this controls whether the user is sent messages after being added to the group. ![image](../images/83ef638-image.png) ### Get Opt Out group ID Once the group is created, we will need to find the Group ID. The Group ID can be obtained via the Contacts API. You can use the group name to search for it. Please see the [documentation](/connect/reference/search-groups) for the "Search for Groups" endpoint for further information. The URL for the request should be similar to this format with account name and full or partial group name substituted with your values. ```text https://contacts.8x8.com/api/v1/accounts//groups?name= ``` ![image](../images/77f1706-image.png) ![image](../images/22c1d2e-image.png) The response body will contain a JSON object, the first item in the array should contain your new group, note down the id which in this case is 11344 but may be different. Note down what your group id is for subsequent steps. ### Pre-Built Template First go to the Automation Builder page and select the blue button for "Create Custom Workflow" ![image](../images/ee8fa19-image.png) ### Opt-Out Template From the pop up in the next page, select "Opt-Out" ![image](../images/048deb9-image.png) ### Building the Workflow In the next page the workflow should be pre-populated with steps, click on the "Trigger" step which is the first step in the flow. Select the subaccount which has the Messaging Apps (Previously known as Chat Apps) number or account that you wish to associate with this opt-out flow. Once it is selected click "Update" to save it to the step. ![image](../images/a82702c-image.png) ### Deciding the Keyword The keyword is controlled by the "Branch" Step in red, you can modify this if you wish to change the condition to trigger the opt out. ![image](../images/02922d4-image.png) > > 📘 Note: The default workflow will try to detect the presence of "STOP" in every message and it is case sensitive. > Once the keyword for opt-out is decided the next step will show the HTTP Request to add a Contact to the blacklisted contact group. ### Add Contact to Contact Group Once that is done, you can move on to modifying the HTTP Request itself. ![image](../images/8e76b72-image.png) The values should mostly be in place, however you will need to replace the request body with the version below which has the Group ID that you saved earlier substituted. | Field | Value | | --- | --- | | Step Name | Any Value | | URL | POST [https://contacts.8x8.com/api/v1/accounts/contacts](https://contacts.8x8.com/api/v1/accounts/contacts) | | Header - Content Type | application/json | | Header - Authorisation | Bearer | | Request Body | {"groups": [{"id": }],"addresses": {"msisdn": "{{data.payload.recipient.channelId}}"}} | Note as usual your API key can be obtained from the API Keys [section](https://connect.8x8.com/messaging/api-keys) of the Connect Dashboard. ### Opt Out Message For Opt Out Messaging, the default message only mentions SMS, however the contact blacklist works across SMS and Messaging Apps (formerly Chat Apps) so you can replace the message with the channels that you use to communicate with the customer. ![image](../images/623e60c-image.png) After saving these changes, the opt out flow can be enabled by enabling the button at the top left and saving. It will activate immediately and any message that contains the keyword will result in a user being added to the opt-out group. ![image](../images/6ab46c8-image.png) ### Testing Once the automation workflow is in place, you can test it by sending the opt-out keyword via SMS to the phone number attached to your subaccount. ### Removing from Group In order to remove a customer from a blacklist, you can either manually remove them by visiting the Contact Groups [page](https://connect.8x8.com/messaging/contacts) by visiting their contact details and change their groups. ![image](../images/fff26d8-image.png) Another option is you can use the [API to remove a contact](/connect/reference/delete-contacts-from-group) from a group. In either case after removing the contact from the Opt-Out group, you can shortly begin sending messages to them again. --- ## Tutorial: Opt Out Keyword (Messaging Apps / WhatsApp) ## Overview This tutorial will show you how to use Automation Builder to build an opt-out flow for WhatsApp using our Messaging Apps API. ## Limitations ### Contacts API operates at the Account-level The **Contacts API** is designed to function at the **Account-level** and does NOT support operation at the **Subaccount** level. Contacts added to blacklist groups will be blacklisted across the **entire Account, affecting all Subaccounts** hence it is not recommended for use cases where you may need to manage separate opt out lists per subaccount. If you need opt-out management at the Subaccount level, you'll have to handle it separately using a third-party system. Please see [this](tutorial-opt-out-keyword-messaging-apps-whatsapp-for-third-party-system) related tutorial for ideas on how to integrate with third-party systems. ### Contacts API blacklist syncs hourly The Contacts API's blacklist sync occurs hourly. Consequently, customers added to a blacklist group may continue to receive messages until the sync process updates our system. ## Tutorial Steps 8x8 has already pre-built an Automation Builder template which allows you to add a keyword which will allow customers to opt out of further communications. We will use this template as the basis of the tutorial. There is setup work we need to do which is covered in the tutorial such as creating the opt-out group via Contacts and obtaining the opt-out group's ID via the Contact API. ### Create Opt-Out Group Visit the [Contacts](https://connect.8x8.com/messaging/contacts) page in Connect where we will create a new group. Once on this page below, click into "Contact Groups" and hit the red **Create Group** button. ![image](../images/2a52aeb-image.png) We should be seeing the screen below, you can fill in any values for the Group Name and Description, but ensure that "Blacklisted" is checked since this controls whether the user is sent messages after being added to the group. ![image](../images/83ef638-image.png) ### Get Opt Out group ID Once the group is created, we will need to find the Group ID. The Group ID can be obtained via the Contacts API. You can use the group name to search for it. Please see the [documentation](/connect/reference/search-groups) for the "Search for Groups" endpoint for further information. The URL for the request should be similar to this format with account name and full or partial group name substituted with your values. ```text https://contacts.8x8.com/api/v1/accounts//groups?name= ``` ![image](../images/77f1706-image.png) ![image](../images/22c1d2e-image.png) The response body will contain a JSON object, the first item in the array should contain your new group, note down the id which in this case is 11344 but may be different. Note down what your group id is for subsequent steps. ### Pre-Built Template First go to the Automation Builder page and select the blue button for "Create Custom Workflow" ![image](../images/ee8fa19-image.png) ### Opt-Out Template From the pop up in the next page, select "Opt-Out" ![image](../images/048deb9-image.png) ### Building the Workflow In the next page the workflow should be pre-populated with steps, click on the "Trigger" step which is the first step in the flow. Select the subaccount which has the Messaging Apps (Previously known as Chat Apps) number or account that you wish to associate with this opt-out flow. Once it is selected click "Update" to save it to the step. ![image](../images/ac16be8-image.png) ### Deciding the Keyword The keyword is controlled by the "Branch" Step in red, you can modify this if you wish to change the condition to trigger the opt out. ![image](../images/adf4912-image.png) > > 📘 Note: The default workflow will try to detect the presence of "STOP" in every message and it is case sensitive. > Once the keyword for opt-out is decided the next step will show the HTTP Request to add a Contact to the blacklisted contact group. ### Add Contact to Contact Group Once that is done, you can move on to modifying the HTTP Request itself. ![image](../images/ff7058a-image.png) The values should mostly be in place, however you will need to replace the request body with the version below which has the Group ID that you saved earlier substituted. | Field | Value | | --- | --- | | Step Name | Any Value | | URL | POST [https://contacts.8x8.com/api/v1/accounts/contacts](https://contacts.8x8.com/api/v1/accounts/contacts) | | Header - Content Type | application/json | | Header - Authorisation | Bearer | | Request Body | {"groups": [{"id": }],"addresses": {"msisdn": "{{data.payload.recipient.channelId}}"}} | Note as usual your API key can be obtained from the API Keys [section](https://connect.8x8.com/messaging/api-keys) of the Connect Dashboard. ### Opt Out Message For Opt Out Messaging, the default message only mentions SMS, however the contact blacklist works across SMS and Messaging Apps (formerly Chat Apps) so you can replace the message with the channels that you use to communicate with the customer. ![image](../images/e84f2ec-image.png) After saving these changes, the opt out flow can be enabled by enabling the button at the top left and saving. It will activate immediately and any message that contains the keyword will result in a user being added to the opt-out group. ![image](../images/6ab46c8-image.png) ### Testing Once the automation workflow is in place, you can test it by sending the opt-out keyword via the channel of your choice. The number you send to should be tied to the subaccount in the workflow, it will not apply across all subaccounts. ![image](../images/4e2737c-image.png) Your customer should see the message and then afterwards no messages can be sent to the user via our platform. ### Removing from Group In order to remove a customer from a blacklist, you can either manually remove them by visiting the Contact Groups [page](https://connect.8x8.com/messaging/contacts) by visiting their contact details and change their groups. ![image](../images/fff26d8-image.png) Another option is you can use the [API to remove a contact](/connect/reference/delete-contacts-from-group)from a group. In either case after removing the contact from the Opt-Out group, you can shortly begin sending messages to them again. --- ## Tutorial: Building a SMS + ChatApps Reservation Menu ## Overview This tutorial will show how to build a simple reservation menu with the Automation Builder UI. This simulates many common actions that a customer would want to have access to over a support channel. The steps in the tutorial is tailored for our Chat Apps API and it is using WhatsApp as the channel in the tutorial However most of the steps will be the same for SMS or other Chat Apps channels. The differences will be in the **ChatApps Trigger** and the **Chat Apps Message Step** where they should be set to the appropriate Chat Apps Channel (or replaced with the **SMS Step** / **SMS Trigger**) and relevant message template. ## Workflow Definition Steps and Explanation 1. **ChatApps Trigger:** When an inbound ChatApps message is received to a number tied to this subaccount a workflow instance is called. This particular flow is for a WhatsApp Message. 2. **Chat Apps Message Step:** A WhatsApp message is sent back to the original sender. 3. **Wait For Reply Step:** Wait up to 5 Minutes for a response. 4. **Branch Step:** If a response is received, redirect it based on if the user responds with a message containing either: 5a. **Branch Step Option:** *Confirm* for confirmation of a reservation. * 6a. **Chat Apps Message Step:** Send Confirmation Message. * 7a. **HTTP Step:** Send HTTP Request to third-party API to alert of confirmation. 5b. **Branch Step Option:***Reschedule* for rescheduling a reservation. * 6b. **Chat App Message Step:** Send a message containing a URL to schedule the appointment. 5c. **Branch Step Option:***Help* for requesting for help with a reservation. * 6c. **Send to Converse:** Redirect the conversation to Converse where a live agent can take over the conversation. 4b. **Chat App Message Step:** Send a reply message if there is no response received from the original message sender and end the workflow instance. ![image](../images/696e2be-image.png) ## Workflow Steps In-Depth ### 1. ChatApps Trigger ![image](../images/4883414-image.png) ![image](../images/b56b6c5-image.png) The **ChatApps trigger** here is set to Chat Apps where you should have a subaccount with an already configured ChatApps channel. You should reach out to your respective account manager to complete the setup for your Chat Apps channel if you do not already have a functioning Chat Apps subaccount. ### 2. ChatApps Message Step ![image](../images/9f8a6a5-image.png) ![image](../images/72ee8f9-image.png) In the **Chat Apps Message Step**, the **destination** property can be set to `{{data.payload.user.channelUserId}}` which refers to the original sender which you can use the subsequent Chat Apps Steps as well. For the **Select a existing template or compose a new message** property, if you have an existing WhatsApp template you can select it. If there is no existing template, in WhatsApp's case you can apply for a WhatsApp template through the ChatApps section of your 8x8 Connect Dashboard. The template will then be available for selection once Meta approves it within the Chat App Message Step interface. In this case we have a call to action type of message template with 3 possible selections but you can also use other template types. The **Message** property will either be automatically filled in if you chose to use a template, if you do not wish to use a template, you can simply enter a freeform message body and it will be sent to the user. For the purposes of this tutorial it is fine to use a free form message body which can be copied below. > > Hello, this is a reminder about your scheduled appointment tomorrow. > > > Choose from the following options: > > > Click Confirm to confirm your appointment. > > > Click Reschedule to be shared a link to reschedule your appointment. > > > Click Help to be chat with an agent for further assistance. > > > ### 3. Wait for Reply Step ![image](../images/8f2479f-image.png) ![image](../images/4b3e879-image.png) In the **Wait for Reply Step**, the **Channel** property should be set to the channel you expect to receive a message in. In the example we are using WhatsApp. The **From** property can be set to {{data.payload.user.channelUserId}} which will refer to the original sender. **Timeout** can be set to an appropriate value for your use case. ### 4. Branch Step (and 5a-c for Branch options) ![image](../images/bd9f581-image.png) ![image](../images/5111726-image.png) In the **Branch Step**, for **Please select a condition** property, set it to "Check if a value contains a string". The **Value to check** property can be set to {{data.waitforreply_9959_step_text}}. Note that the Step name for waitforreply_9959_step_text may change in your workflow definition and should be set to the Step Name in your workflow invocation. For the **keyword(s) to compare** property, add "Confirm", "Reschedule" and "Help" to add three options to the branch. ### 4b. Chat Apps Message ![image](../images/1a907cc-image.png) ![image](../images/884f9c0-image.png) In the **Chat Apps Message Step**, set "Please select a new subaccount" to your subaccount that should already be configured for the Chat Apps channel that you would like to use. **Destination** can be set to {{data.payload.user.channelUserId}} to reply to the original sender as in previous Chat App Message steps. Message can be set to a free form message with the following text by selecting "Compose Message" > > No Response within the allowed time, goodbye. > > > ### 6a. ChatApps Message Step (Confirm) ![image](../images/514e30a-image.png) ![image](../images/43ea5f6-image.png) In the **Chat Apps Message Step**, set "Please select a new subaccount" to your subaccount that should already be configured for the Chat Apps channel that you would like to use. **Destination** can be set to {{data.payload.user.channelUserId}} to reply to the original sender as in previous Chat App Message steps. Message can be set to a free form message with the following text by selecting "Compose Message" > > Thank you for confirming, our system has been updated. > > > ### 6b. ChatApps Message Step (Reschedule) ![image](../images/aa1e183-image.png) ![image](../images/6fd6ef8-image.png) In the **Chat Apps Message Step**, set "Please select a new subaccount" to your subaccount that should already be configured for the Chat Apps channel that you would like to use. **Destination** can be set to {{data.payload.user.channelUserId}} to reply to the original sender as in previous Chat App Message steps. Message can be set to a free form message with the following text by selecting "Compose Message" > > Please visit [https://example.com](https://example.com) to reschedule your appointment. > > > ### 6c. Send to Converse ![image](../images/1df8b1d-image.png) The Send to Converse step will send the conversation to [Converse](https://www.google.com/search?q=converse+8x8+docs&rlz=1C5GCEM_enSG1072SG1072&oq=converse+8x8+docs&gs_lcrp=EgZjaHJvbWUyBggAEEUYOTIICAEQABgWGB4yDQgCEAAYhgMYgAQYigUyBggDEEUYQDIGCAQQRRhAMgYIBRBFGEAyBggGEEUYPDIGCAcQRRg80gEIMTY1MGowajSoAgCwAgA&sourceid=chrome&ie=UTF-8) for a live agent to take over. Converse should be configured for your account prior to using this option. ### 7c. HTTP Request ![image](../images/040d2ff-image.png) ![image](../images/9203c68-image.png) Set the **URL** Property to the Endpoint that you would like to send a HTTP request to. Also modify it's method to the appropriate choice for your endpoint. In the example I use a free endpoint from to do mock API testing and see requests come in. The **Header** and **Query** properties can also be modified as needed. I set 1 property in the header to be content-type with the value as application/json. If required you can also change the **Request Body** property, I set the value as below but it is not necessary. ```json { "data": "payload" } ``` **Timeout** property can be set to any appropriate value, I would recommend above 20 seconds to ensure the server has appropriate time to respond. ## Testing the Workflow In order to test the workflow, simply send a WhatsApp message to the WhatsApp account tied to your subaccount. In the example you can send any message to start ![image](../images/8404ac9-image.png) ## Tutorial JSON Automation Builder UI features a function to import workflow definitions for easy versioning and sharing. If you would like to import the example for this tutorial, save the text below as a file such as tutorial_automation_builder.json and import it from the Automation Builder UI. ```json { "definition": { "id": "3ca6c9ce-603e-4015-9aab-29047b327e7f", "name": "RS 3 Options Test", "version": 2, "steps": [ { "stepType": "ChatAppsMessage", "id": "chatappsmessage_6635", "do": [], "nextStepId": "waitforreply_9959", "inputs": { "subAccountId": "InternalDemoCPaaS_WhatsApp", "type": "template", "content": { "template": { "name": "rs_appointment_confirmation", "components": [ { "type": "button", "parameters": [ { "type": "payload", "payload": "Confirm-Button-Payload", "text": "Confirm" } ], "index": 0, "subType": "QuickReply" }, { "type": "button", "parameters": [ { "type": "payload", "payload": "Reschedule-Button-Payload", "text": "Reschedule" } ], "index": 1, "subType": "QuickReply" }, { "type": "button", "parameters": [ { "type": "payload", "payload": "Help-Button-Payload", "text": "Help" } ], "index": 2, "subType": "QuickReply" } ], "language": "en_US" } }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "WaitForReply", "id": "waitforreply_9959", "do": [], "nextStepId": null, "inputs": { "from": "{{data.payload.user.channelUserId}}", "channel": "whatsapp", "timeout": "0.00:05:00" }, "outputs": { "waitforreply_9959_step_text": "{{step.reply.payload.content.text}}" }, "selectNextStep": { "branch_7003": "{{ step.reply != null }}", "chatappsmessage_7447": "{{ step.reply == null }}" } }, { "stepType": "Branch", "id": "branch_7003", "do": [], "nextStepId": null, "inputs": {}, "outputs": {}, "selectNextStep": { "chatappsmessage_2979": "{{stringContains(data.waitforreply_9959_step_text, 'Confirm', true)}}", "chatappsmessage_5197": "{{stringContains(data.waitforreply_9959_step_text, 'Reschedule', true)}}", "sendtoconverse_6641": "{{stringContains(data.waitforreply_9959_step_text, 'Help', true)}}" } }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_7447", "do": [], "nextStepId": null, "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "type": "text", "content": { "text": "No response within the allowed time, goodbye." }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "SendToConverse", "id": "sendtoconverse_6641", "do": [], "nextStepId": null, "inputs": { "payload": "{{data.payload}}" }, "outputs": { "sendtoconverse_6641_ticketId": "{{step.ticketId}}" }, "selectNextStep": {} }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_5197", "do": [], "nextStepId": null, "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "type": "text", "content": { "text": "Please visit https://example.com to reschedule your appointment." }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_2979", "do": [], "nextStepId": "httprequest_3392", "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "type": "text", "content": { "text": "Thank you for confirming, our system has been updated." }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "HttpRequest", "id": "httprequest_3392", "do": [], "nextStepId": null, "inputs": { "headers": { "content-Type": "application/json" }, "method": "POST", "url": "https://rs8x8-voice-testing.free.beeceptor.com", "parameters": {}, "body": { "data": "payload" }, "timeoutSeconds": 19 }, "outputs": {}, "selectNextStep": {} } ] }, "subAccountId": "InternalDemoCPaaS_ChatApps", "trigger": "inbound_chat_apps", "status": "disabled" } ``` --- ## Batch SMS ## Tutorial: Learn how to use API for batch SMS ### Introduction 8x8 offers different API methods that allow you to send SMS programmatically. In this tutorial, we are going to cover the bulk SMS method: [Send SMS batch](/connect/reference/send-sms-batch). You can also send a single SMS in one command - for more information, check the following methods in the documentation: [Send SMS API](/connect/docs/send-sms-api-reference) If you follow the different steps of this tutorial, you will get to send a batch of SMS directly from your command line utility using a simple curl command. ### Index * Learn how to use the method for sending batch SMS * Prerequisites * Account and credentials * Signing-up * Finding your apiKey bearer token (for API authentication) * Identifying your 8x8 SubaccountId * API request * Preparing the URL * Preparing the authentication * Preparing the data payload * Putting it together and posting the curl request * Going further * API response * API errors In this tutorial, we are going to send a (small) batch of SMS at once by contacting two recipients in one API request using curl. For this, we are going to use an 8x8 account created with our email: `user@example.com`. We are going to use our subaccountid `acme_corp`. The apiKey for our account is `5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg`. ### Prerequisites * Command line interface compatible with CURL * 8x8 CPaaS account * apiKey (Bearer token) * 8x8 CPaaS subaccountid * Destination phone number * SMS Body --- ### Account and credentials *You will need to sign-up to use the API. The following steps will guide you through this process and highlight the information to keep aside.* #### I. Signing-up 1. Head to [8x8 Connect sign-up page](https://connect.8x8.com/login/signup) 2. Enter your email and follow the instructions to define your password and finalize your account (by default, API password and account password are the same, you can modify this from your account settings) 3. Confirm your email address by clicking on the validation link you received in the activation email to activate your account. ![Signup 8x8 connect](../images/09a33d5-Signup_8x8_connect.png "Signup 8x8 connect.png") #### II. Finding your apiKey bearer token (for API authentication) 1. Head to . 2. Click on LOG IN. 3. Enter your email address and password to get access to your account dashboard. 4. Head over to the **side menu > API keys** section 5. Create an API key if empty and then keep the API Key value, here: `5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg` ![image](../images/689017a-API_2.png "API 2.png") #### III. Identifying your Subaccountid 1. Head over to the pricing section and use the subaccountid list to retrieve the `subaccountid` that you want to use 2. By default, your account comes with only one `subaccountid` for your high-quality service. It is designated by your `accountid` and the suffix `_hq`. 3. Note down this value, you will need it later. 4. In that example, the `subaccountid` is `acme_corp` ![image](../images/b0ec3e5-API_3.png "API 3.png") --- ### API Request The 8x8 SMS batch method expects requests sent by developers to respect a specific format. In the following parts, we are going to go over the different elements of the request: * the URL format * the authentication * the data payload. At the end of the section, we will generate a curl command to send an SMS directly from the command line. #### I. Preparing the request URL ##### Remarks * We are going to send a POST request to the 8x8 API batch URL endpoint. * As detailed in the [Send SMS batch](/connect/reference/send-sms-batch), the URL is defined by the following pattern: `https://sms.8x8.com/api/v1/subaccounts/{subAccountId}/messages/batch` ##### Tutorial URL * In order to create the URL to use, we are going to replace `{subaccountid}` in the pattern above by `acme_corp`, the subaccountid that we are using in this tutorial * In that example, the URL that we are going to send the request to is: `https://sms.8x8.com/api/v1/subaccounts/acme_corp/messages/batch` ##### Platform Deployment Region * To ensure the use of the correct platform deployment region, it is necessary to modify the base URL to correspond with the provisioned region of your account. Refer to the table below for the appropriate base URL associated with each platform region: | URL | Region | | --- | --- | | [https://sms.8x8.com](https://sms.8x8.com) | Asia Pacific (default) | | [https://sms.us.8x8.com](https://sms.us.8x8.com) | North America | | [https://sms.8x8.uk](https://sms.8x8.uk) | Europe | | [https://sms.8x8.id](https://sms.8x8.id) | Indonesia | * For more information on platform deployment regions, please visit the following [page](/connect/docs/platform-deployment-regions). ##### curl * In curl, we will have to indicate that we want to do a POST request to this URL by using the following command: ```bash curl -X "POST" https://sms.8x8.com/api/v1/subaccounts/acme_corp/messages/batch ``` #### II. Preparing the request authentication #### Remarks * As explained in the [Authentication](/connect/docs/authentication), the API authentication uses an apiKey bearer token method. #### Tutorial authentication * In this tutorial, the apiKey for our account is `5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg`. #### curl * In a curl request, bearer tokens must be passed as a header like so: `-H "Authorization: Bearer {token}"` * We just have to replace our `{token}` placeholder by our apiKey * The authorization header will then look like that: `-H "Authorization: Bearer 5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg"` #### III. Preparing the request data payload * The API expects to receive a structured request containing the SMS data in a specific format. * As detailed in the documentation, the data that we have to submit should be a JSON object structured as follow: ![image](../images/c1568db-API_4.bmp "API 4.bmp") #### Tutorial request data payload * The API expects to receive a structured request containing the SMS details and parameters. The format of the request is JSON and it can accept both optional and required parameters. * For simplicity sake, we are going to use only the most important of the parameters (the others are detailed in the documentation): * **Messages**: * Array containing multiple SmsRequest objects (see Single doc) * Here we're going to send one template message and one non-template message * For the template message, only the destination is mandatory, the source and text can be found below in the **Template** object * For the non-template message, we specify the text `"Bob, special present for you from Santa"` which differs from the text in the Template * **Template**: * Object applying common properties to the SmsRequest objects in messages * For a generic greeting, let’s use the value: `"Happy New Year!"` * **Encoding**: * This parameter tells the destination handset which encoding to use to display the SMS. Our text only contains GSM7bit characters which is the most standard encoding but for the sake of safety let’s use `"AUTO"` * it means that the API will automatically detect the characters used in the text and select the best encoding. * It prevents the message from showing up as “ ⃞ ⃞ ⃞ ⃞, ⃞ ⃞ ⃞ ⃞” in the case where we would have used special characters or another alphabet.Our final JSON object that we are going to send as the request data payload is then: ```json { "messages": [ { "destination": "+6500000000", }, { "destination": "+6500000001", "text": "Bob, special present for you from Santa" } ], "template": { "source": "Acme Corp", "text": "Happy New Year!", "encoding": "AUTO" } } ``` #### curl * In curl, we will transmit the JSON data inline and indicate that the data payload is in JSON format using the following commands: ```bash -H "Content-Type: application/json" -d $'{ "messages": [ { "destination": "+6500000000", }, { "destination": "+6500000001", "text": "Bob, a special present for you from Santa" } ], "template": { "source": "Acme Corp", "text": "Happy New Year!", "encoding": "AUTO" } }' ``` #### IV. Putting it together and posting the curl request * If we wrap up all the elements prepared in the steps above, we should put together the 3 elements of our request: **URL + Authentication + Data Payload** * To send the API request to 8x8 API batch endpoint with our message we should use the following command in our command line utility: ```bash curl -X "POST" https://sms.8x8.com/api/v1/subaccounts/amazing_hq/messages/batch -H "Authorization: Bearer 5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg" -H "Content-Type:application/json" -d $'{ "messages": [{"destination": "+6500000000"}, {"destination": "+6500000001","text": "Bob, special present for you from Santa"}], "template": { "source": "Acme Corp", "text": "Happy New Year!","encoding": "AUTO" } }' ``` * And that’s it! Here is the result: ![image](../images/cc6dccb-API_5.bmp "API 5.bmp") #### Going further #### I. API response * When sending the curl command above from your command line utility, you notice that the 8x8 API sends back a response that shows up in your terminal, for example in this tutorial: ```json { "batchId":"1328cb94-e714-eb11-8278-00155d9f27ac", "clientBatchId":null, "acceptedCount":2, "rejectedCount":0, "messages":null } ``` * The API response is used to provide feedback about the expected result of the API request (sending an SMS) and various additional information. * If we take some time to analyze the different elements there, we can identify the following: * **umid**: it stands for unique message id, it is the unique id associated with this SMS by 8x8 CPaaS platform. * **clientmessageid**: here, it is null because no value has been specified in the request but you have the possibility to attribute your own custom ids to your messages. * **destination**: this is the phone number to which the SMS was sent. * **status**: this array contains 2 elements: the status of the message and the description of this status * For more information, check the dedicated section of the [Send SMS batch](/connect/reference/send-sms-batch), "Responses" section #### II. API errors * A developer World without error would not be funny! * If ever you send malformed requests to the API, it will let you know by using some specific error codes in the response. * You can find the different codes and their meanings in the [API Error codes](/connect/reference/api-error-codes) --- ## Tutorial: Building a WhatsApp + Google Calendar Chat Bot This tutorial will show you how to build a WhatsApp + Google Calendar chat bot using 8x8 Automation Builder. The resulting chat bot will allow customers to send a message to a WhatsApp Business Account tied to your 8x8 subaccount and schedule an appointment on Google Calendar. The concepts in this guide are useful as a stepping stone to understand how Automation Builder can be used with other systems that expose APIs to create richer experiences with the 8x8 Chat Apps API. ## Overview There are two components to this project: 1. **Automation Builder workflow** which will be built on the [Connect](https://connect.8x8.com/) Dashboard. The workflow takes care of orchestrating responses through WhatsApp. 2. **Backend Server** we have provided example server [code](https://github.com/EMChamp/8x8-google-calendar-wa-scheduler) in Python for the Scheduling service described in this diagram. The Backend Server API allows us to connect to the Google Calendar API and also run some scheduling logic based on the responses. ![image](../images/3d46132-image.png) ## Demo Video Please see a demo video of the WhatsApp Chatbot below. ## Automation Builder Workflow Steps Here is a visual overview of the complete automation builder workflow, we will cover each step in depth in the following section of the tutorial. 1. Customer messages WhatsApp Business Account which triggers the workflow. 2. Chat Apps Message Sends Main Menu to Customer 3. Wait for Customer Reply 4. Check for customer response and branch based on that. 5. Respond according to user's chosen option a. Prompt user for Email Address b. Send Goodbye message if flow is cancelled. 6. Wait for user reply to email address, in order to save as an output variable for use later in workflow. 7. Send API request to our Backend API to check for available Calendar Timeslots. 8. Share Available Timeslots through WhatsApp as a list. 9. Wait for customer to choose available timeslot from list. 10. Branch based on user's chosen timeslot 11. a/b/c. Send API request to book timeslot based on user's chosen timeslot. d. Send Cancellation Message through WhatsApp. 12. a/b/c. Send Confirmation message based on user's chosen timeslot. ![image](../images/77b8cc0-image.png) ![image](../images/1dcaabf-image.png) ![image](../images/e0744e4-image.png) ## Workflow Steps In-Depth ### 1) Chat Apps Trigger This trigger is responsible for kicking off the workflow, by default it will be triggered when any inbound chat apps message is received on the WABA tied to the subaccount you select below. ![image](../images/4883414-image.png) ![image](../images/b56b6c5-image.png) | Field | Value | | --- | --- | | Trigger | Inbound Chat Apps | | Please select a new subaccount | Select the subaccount with the WABA you would like to send from. | ### 2) Chat Apps Message Step - Main Menu This Chat Apps Message Step introduces the main menu that will start off with a simple welcome message and then display a menu with 2 options for the customer to select from. ![image](../images/f0af7e5-image.png) ![image](../images/9327f92-image.png) | Field | Value | | --- | --- | | Step Name | This is autofilled, the step name can optionally be changed for easier readability.**Note:** For the sake of brevity, we will ignore the Step Name field for subsequent steps in this tutorial as the recommendation is the same for all steps. | | Please select a new subaccount | Select the subaccount with the WABA you would like to send from. | | Destination | `{{data.payload.user.channelUserId}}`**Note:** This allows us to reply to the user who sent the WhatsApp message, which is what we want in a chatbot scenario. | | Select an existing template or compose a new message | Welcome to 8x8's WhatsApp - Google Calendar Demo. This demo will schedule a Google Calendar event with one of our specialists through WhatsApp.Please choose from the following options:1. Schedule a 1 hour meeting2. Exit | ### 3) Wait for Reply Step - Reply to Main Menu This step waits for a customer to reply to the main menu option list, their reply will be used in the following branch step. ![image](../images/8f2479f-image.png) ![image](../images/4b3e879-image.png) As configured this waits 5 minutes for a customer to reply to the main menu option message in the previous step, however you can change the timeout as desired. | Field | Value | | --- | --- | | From | `{{data.payload.user.channelUserId}}` | | Please select a channel | WhatsApp | | Timeout | 5 minutes | ### 4) Branch Step In this step, we branch off two separate paths depending on the response from the WA user. ![image](../images/c548fe6-image.png) ![image](../images/fdd93b7-image.png) | Field | Value | | --- | --- | | Please select a condition | Check if a value contains a string | | Value to Check | {{data.waitforreply_XXXX_step_text}}Note: Replace XXXX with the number identifier for your previous waitforreply step, this will be auto generated by the system and will be different for your specific workflow. | | Keywords to Compare | 1, 2 | ### 5a) Chat Apps Message - Enter Email Address In this Chat Apps message step we will send a very simple prompt to ask the user to input their email address. ![image](../images/6065d97-image.png) ![image](../images/5cf7320-image.png) | Field | Value | | --- | --- | | Destination | {{data.payload.user.channelUserId}} | | Select an existing template or compose a new message | Check Compose Message | | Message | Please enter your email addres | ### 5b) Chat Apps Message - Cancellation Message This step is if the user chooses not to schedule a meeting in the main menu. In that case a cancellation message will be played for the user. ![image](../images/c7348d6-image.png) ![image](../images/b9ec4d8-image.png) | Field | Value | | --- | --- | | Please select a new subaccount | Select the subaccount with the WABA you would like to send from. | | Destination | {{data.payload.user.channelUserId}} | | Message | Goodbye! | ### 6) Wait for Reply - Email Address In the wait for reply step, we will wait for the customer to enter their email address. We give them 1 minute to respond but you can change this value as needed. ![image](../images/933c523-image.png) | Field | Value | | --- | --- | | From | {{data.payload.user.channelUserId}} | | Please select a channel | WhatsApp | | Timeout | 1 Minute | ### 7) HTTP Request Step - Check for available time slots This is the first of our HTTP Request Steps that will be hitting our **/retrieve_meetings** endpoint that is part of our API. This endpoint will return possible meeting timeslots which can then be presented to the user in the subsequent Chat Apps Message Step. ![image](../images/e70ee19-image.png) | Field | Value | | --- | --- | | URL | This should be your server's publically accessible URL | | Header | content-type: application/json | | Request Body | {} | The outputs of this step is how we save the values from the API response body to be presented to the user. The values match the response body JSON that we will specify in our code. ![image](../images/06d0917-image.png) | Field | Value | | --- | --- | | meetings | {{step.responseBody.date_options}} | | timeslot1_start | {{step.responseBody.timeslot1_start}} | | timeslot1_end | {{step.responseBody.timeslot1_end}} | | timeslot2_start | {{step.responseBody.timeslot2_start}} | | timeslot2_end | {{step.responseBody.timeslot2_end}} | | timeslot3_start | {{step.responseBody.timeslot3_start}} | | customer_email | {{data.waitforreply_9196_step_text}} | ### 8) Chat Apps Message Step - Meeting Timeslots ![image](../images/bb9426a-image.png) | Field | Value | | | --- | --- | --- | | Please select a new subaccount | Select the subaccount with the WABA you would like to send from. | | | Destination | {{data.payload.user.channelUserId}} | | | Select an existing template or compose a new message | Compose Message | | | Message | {{'Here are the available timeslots. Please enter the number for the option you would like to book. \n'+ data.meetings + '\nOption 4: Exit Scheduling'}} | | A bit of an explanation for the **message** field, the entire message is wrapped in curly brackets since we are using the output data.meetings as part of the string. This is combined with the rest of the string in order to produce a message which looks like this: ![image](../images/3ace9ed-image.png) ### 9) Wait For Reply Step - Customer Chooses Timeslot This step will wait for a customer to choose from the options presented above. ![image](../images/d045c6b-image.png) | Field | Value | | --- | --- | | From | {{data.payload.user.channelUserId}} | | Please select a Channel | WhatsApp | | Timeout | 1 Minute | ### 10) Branch Step - Branch based on Chosen Timeslot This step will branch based on the customer's input from the previous step. The following steps are HTTP Request steps that will decide what meeting timeslot is booked. ![image](../images/a9ad033-image.png) ![image](../images/cf0e856-image.png) | Field | Value | | --- | --- | | Please select a condition | Check if a value contains a string | | Value to Check | {{data.waitforreply_XXXX_step_text}} | | Keyword(s) to compare | 1,2,3,4 | ### 11a,b,c) HTTP Request Step - Reserve Calendar Timeslot Based on the User's Input These three HTTP Request Steps will be tied to the customer's input in the previous step. ![image](../images/7df91f8-image.png) The only difference between these fields will be the numbers used for the timeslot, so the backend server knows what the user chose. For example, we will use {{data.timeslot1_start}} for timeslot 1's start time and then {{data.timeslot2_start}} for timeslot 1's start time. ![image](../images/7a7c7c7-image.png) | Field | Value | | | --- | --- | --- | | URL | Your Backend Server's URL | | | Request Body | {"start": "{{data.timeslotX_start}}","end": "{{data.timeslotX_end}}","customer_email": "{{data.waitforreply_9196_step_text}}"}**Note:** Replace timeslotX with the timeslot option, such as timeslot1, timeslot2, timeslot3. | | | Timeout | 20 Seconds | | ![image](../images/b4cc48a-image.png) For the output you can save the start_time and end_time for the following confirmation message. The values will be specify to JSON response body of the backend API code that we provide. | Field | Value | | --- | --- | | start_time | {{step.responseBody.start.dateTime}} | | end_time | {{step.responseBody.end.dateTime}} | ### 11d) Chat Message Step - Cancellation message This step is if the user chooses not to schedule a meeting in the main menu, in that case a cancellation message is played. ![image](../images/c7348d6-image.png) ![image](../images/b9ec4d8-image.png) | Field | Value | | --- | --- | | Please select a new subaccount | Select the subaccount with the WABA you would like to send from. | | Destination | {{data.payload.user.channelUserId}} | | Message | Goodbye! | ### 12a,b,c) Chat Apps Message - Calendar Invite Confirmation Message This step sends a simple confirmation message through WhatsApp and ends the workflow. If a user sends another message after this workflow ends, then it will trigger a fresh workflow instances from the beginning. ![image](../images/ac714d3-image.png) ![image](../images/d9d84de-image.png) | Field | Value | | --- | --- | | Please select a new subaccount | Select the subaccount with the WABA you would like to send from. | | Destination | {{data.payload.user.channelUserId}} | | Message | Goodbye! | ## Automation Builder - Example JSON If you would like to skip building the steps manually above, we have provided an example JSON file to get you started which you can import into Automation Builder. Note that you will have to modify values in the steps according to your specific backend system and also your 8x8 subaccount. Automation Builder JSON ```json { "definition": { "id": "d2f47259-75ff-45bd-950e-97a5dee631e4", "name": "RS WhatsApp GCal Demo", "version": 25, "steps": [ { "stepType": "Branch", "id": "branch_6057", "do": [], "nextStepId": null, "inputs": {}, "outputs": {}, "selectNextStep": { "chatappsmessage_5822": "{{stringContains(data.payload.content.text, 'RSGoogleCalendarDemo', true)}}" } }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_5822", "do": [], "nextStepId": "waitforreply_8622", "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "type": "text", "content": { "text": "Welcome to 8x8's WhatsApp - Google Calendar Demo. This demo will schedule a Google Calendar event with one of our specialists through WhatsApp.\n\nPlease choose from the following options:\n1) Schedule a 1 hour meeting\n2) Exit" }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "WaitForReply", "id": "waitforreply_8622", "do": [], "nextStepId": null, "inputs": { "from": "{{data.payload.user.channelUserId}}", "channel": "whatsapp", "timeout": "0.00:01:00" }, "outputs": { "waitforreply_8622_step_text": "{{step.reply.payload.content.text}}" }, "selectNextStep": { "branch_8338": "{{ step.reply != null }}", "chatappsmessage_6652": "{{ step.reply == null }}" } }, { "stepType": "Branch", "id": "branch_8338", "do": [], "nextStepId": null, "inputs": {}, "outputs": {}, "selectNextStep": { "chatappsmessage_0501": "{{stringContains(data.waitforreply_8622_step_text, '1', true)}}", "chatappsmessage_9836": "{{stringContains(data.waitforreply_8622_step_text, '2', true)}}" } }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_6652", "do": [], "nextStepId": null, "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "type": "text", "content": { "text": "No Reply Detected within the allotted time, please try again." }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_9836", "do": [], "nextStepId": null, "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "type": "text", "content": { "text": "Goodbye!" }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_0501", "do": [], "nextStepId": "waitforreply_9196", "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "type": "text", "content": { "text": "Please enter your email address:" }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "WaitForReply", "id": "waitforreply_9196", "do": [], "nextStepId": null, "inputs": { "from": "{{data.payload.user.channelUserId}}", "channel": "whatsapp", "timeout": "0.00:01:00" }, "outputs": { "waitforreply_9196_step_text": "{{step.reply.payload.content.text}}" }, "selectNextStep": { "httprequest_0590": "{{ step.reply != null }}" } }, { "stepType": "HttpRequest", "id": "httprequest_0590", "do": [], "nextStepId": "chatappsmessage_7792", "inputs": { "method": "GET", "url": "https://rsunga.ngrok.io/retrieve_meetings", "headers": { "content-Type": "application/json" }, "parameters": {}, "body": {}, "timeoutSeconds": 20 }, "outputs": { "meetings": "{{step.responseBody.date_options}}", "timeslot1_start": "{{step.responseBody.timeslot1_start}}", "timeslot1_end": "{{step.responseBody.timeslot1_end}}", "timeslot2_start": "{{step.responseBody.timeslot2_start}}", "timeslot2_end": "{{step.responseBody.timeslot2_end}}", "timeslot3_start": "{{step.responseBody.timeslot3_start}}", "timeslot3_end": "{{step.responseBody.timeslot3_end}}", "customer_email": "{{data.waitforreply_9196_step_text}}" }, "selectNextStep": {} }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_7792", "do": [], "nextStepId": "waitforreply_3750", "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "type": "text", "content": { "text": "{{'Here are the available timeslots. Please enter the number for the option you would like to book. \\n'+ data.meetings + '\\nOption 4: Exit Scheduling'}}" }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "WaitForReply", "id": "waitforreply_3750", "do": [], "nextStepId": null, "inputs": { "from": "{{data.payload.user.channelUserId}}", "channel": "whatsapp", "timeout": "0.00:01:00" }, "outputs": { "waitforreply_3750_step_text": "{{step.reply.payload.content.text}}" }, "selectNextStep": { "branch_3014": "{{ step.reply != null }}" } }, { "stepType": "Branch", "id": "branch_3014", "do": [], "nextStepId": null, "inputs": {}, "outputs": {}, "selectNextStep": { "httprequest_1190": "{{stringContains(data.waitforreply_3750_step_text, '1', true)}}", "httprequest_7087": "{{stringContains(data.waitforreply_3750_step_text, '2', true)}}", "httprequest_4260": "{{stringContains(data.waitforreply_3750_step_text, '3', true)}}", "chatappsmessage_4941": "{{stringContains(data.waitforreply_3750_step_text, '4', true)}}" } }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_4941", "do": [], "nextStepId": null, "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "type": "text", "content": { "text": "Goodbye!" }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "HttpRequest", "id": "httprequest_4260", "do": [], "nextStepId": "chatappsmessage_1276", "inputs": { "headers": { "content-Type": "application/json" }, "method": "POST", "url": "https://rsunga.ngrok.io/create_meeting", "parameters": {}, "body": { "start": "{{data.timeslot3_start}}", "end": "{{data.timeslot3_end}}", "customer_email": "{{data.waitforreply_9196_step_text}}" }, "timeoutSeconds": 20 }, "outputs": { "start_time": "{{step.responseBody.start.dateTime}}", "end_time": "{{step.responseBody.end.dateTime}}" }, "selectNextStep": {} }, { "stepType": "HttpRequest", "id": "httprequest_7087", "do": [], "nextStepId": "chatappsmessage_1074", "inputs": { "headers": { "content-Type": "application/json" }, "method": "POST", "url": "https://rsunga.ngrok.io/create_meeting", "parameters": {}, "body": { "start": "{{data.timeslot2_start}}", "end": "{{data.timeslot2_end}}", "customer_email": "{{data.waitforreply_9196_step_text}}" }, "timeoutSeconds": 20 }, "outputs": { "start_time": "{{step.responseBody.start.dateTime}}", "end_time": "{{step.responseBody.end.dateTime}}" }, "selectNextStep": {} }, { "stepType": "HttpRequest", "id": "httprequest_1190", "do": [], "nextStepId": "chatappsmessage_4345", "inputs": { "method": "POST", "url": "https://rsunga.ngrok.io/create_meeting", "headers": { "content-Type": "application/json" }, "parameters": {}, "body": { "start": "{{data.timeslot1_start}}", "end": "{{data.timeslot1_end}}", "customer_email": "{{data.waitforreply_9196_step_text}}" }, "timeoutSeconds": 20 }, "outputs": { "start_time": "{{step.responseBody.start.dateTime}}", "end_time": "{{step.responseBody.end.dateTime}}" }, "selectNextStep": {} }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_1276", "do": [], "nextStepId": null, "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "type": "text", "content": { "text": "Your meeting has been booked, please check your email for further details." }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_1074", "do": [], "nextStepId": null, "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "type": "text", "content": { "text": "Your meeting has been booked, please check your email for further details." }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_4345", "do": [], "nextStepId": null, "inputs": { "subAccountId": "InternalDemoCPaaS_ChatApps", "type": "text", "content": { "text": "Your meeting has been booked, please check your email for further details.\n" }, "user": { "msisdn": "{{data.payload.user.channelUserId}}" } }, "outputs": {}, "selectNextStep": {} } ] }, "subAccountId": "InternalDemoCPaaS_ChatApps", "trigger": "inbound_chat_apps", "status": "disabled" } ``` ## Backend Server - Example Code Please see the following [repository](https://github.com/EMChamp/8x8-google-calendar-wa-scheduler) for the sample backend code. This includes instructions on how to run the server locally. You will need to expose it to the public internet so that Automation Builder can call the API. While this is assumed knowledge and beyond the scope of the tutorial, we used a service from to expose the server running locally for demonstration purposes. ### Code Explanation - API ```python from flask import Flask, request from google_calendar_create_event import create_event from google_calendar_retrieve_events import retrieve_events app = Flask(__name__) @app.route("/create_meeting", methods=['POST']) def create_meeting(): # Retrieve JSON data from the request body request_data = request.get_json() # Extract the "start" and "end" parameters from the JSON data start = request_data.get('start') end = request_data.get('end') customer_email = request_data.get('customer_email') return create_event(start, end, customer_email) @app.route("/retrieve_meetings") def retrieve_meetings(): return retrieve_events() if __name__ == '__main__': app.run(debug=True, port=5003) ``` This is the API code, it exposes two endpoints: * The **/create_meeting** endpoint which will gather information about which meeting timeslot to book. * The **/retrieve_meetings** endpoint which will return available timeslots from a user's google calendar. In this example, we hardcode the Google Calendar that we want to book meetings to in our config file. Also note, that this tutorial assumes that you have access rights to the Google Calendar API for that user. ### Code Explanation - Retrieve Events This code is responsible for retrieving events from your chosen Google Calendar user's calendar in order to find out which timeslots are free. These are returned in lines 135-144 as a human readable text to be printed by Automation Builder in **Step 8)** in the tutorial. Please note it requires the use of credentials for the Google Calendar API in line 80. Google has a [quickstart](https://developers.google.com/calendar/api/quickstart/python) on getting started with the Google Calendar API which we used as the basis for this backend code. Please read through their quick start in order to generate the credentials.json which will be used as the google_api_creds.json below. Also please note, the code uses the Singapore timezone by default in line 93 which should be changed according to your google calendar user's timezone. Retrieve Events ```python from __future__ import print_function import datetime import os.path import json from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. SCOPES = ['https://www.googleapis.com/auth/calendar'] # Function to format a datetime object as a human-readable string def format_datetime(dt): return dt.strftime('%A, %d %B %Y %H:%M') # Function to check if a date is a weekday (Monday to Friday) def is_weekday(date): return date.weekday() < 5 # Monday to Friday are 0 to 4 # Function to format time slots def format_time_slot(slot, option_number): start_time = datetime.fromisoformat(slot["start"]) end_time = datetime.fromisoformat(slot["end"]) formatted_option = f"Option {option_number}) {start_time.strftime('%B %d, %I%p')} - {end_time.strftime('%I%p')}" return formatted_option def get_next_weekday(now_singapore): # Check if it's a weekday and within the 9 AM to 6 PM time range if is_weekday(now_singapore) and now_singapore.hour < 9: # If today is a weekday and within the specified time range, use the current time start_singapore = now_singapore.replace(hour=9, minute=0, second=0, microsecond=0) elif is_weekday(now_singapore) and now_singapore.hour >=18: # Determine the number of days to add to get to the next weekday if now_singapore.weekday() in [0, 1, 2, 3]: next_weekday = now_singapore + datetime.timedelta(days=1) else: next_weekday = now_singapore + datetime.timedelta(days=3) # Set the start and end times for the next weekday start_singapore = next_weekday.replace(hour=9, minute=0, second=0, microsecond=0) elif is_weekday(now_singapore) and 9 <= now_singapore.hour < 18: #Pick the next timeslot at least an hour away next_weekday = now_singapore + datetime.timedelta(hours=1) #This needs to be one hour or the logic for retrieve events will break start_singapore = next_weekday.replace(minute=0, second=0, microsecond=0) else: if now_singapore.weekday() == 5: next_weekday = now_singapore + datetime.timedelta(days=2) else: next_weekday = now_singapore + datetime.timedelta(days=1) start_singapore = next_weekday.replace(hour=9, minute=0, second=0, microsecond=0) return start_singapore def is_valid_timeslot(timeslot): if is_weekday(timeslot) and 9 <= timeslot.hour < 18: return True else: return False def retrieve_events(): """Shows basic usage of the Google Calendar API. Prints the start and name of the next 10 events on the user's calendar. """ creds = None # The file token.json stores the user's access and refresh tokens, and is # created automatically when the authorization flow completes for the first # time. if os.path.exists('token.json'): creds = Credentials.from_authorized_user_file('token.json', SCOPES) # If there are no (valid) credentials available, let the user log in. if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file( 'google_api_creds.json', SCOPES) creds = flow.run_local_server(port=0) # Save the credentials for the next run with open('token.json', 'w') as token: token.write(creds.to_json()) try: # Build Google calendar Service service = build('calendar', 'v3', credentials=creds) # Get current time in Singapore timezone singapore_timezone = datetime.timezone(datetime.timedelta(hours=8)) # UTC+8 now_singapore = datetime.datetime.now(singapore_timezone) # Get Start of next Weekday start_singapore = get_next_weekday(now_singapore) # Create a list to store available time slots available_time_slots = [] # Create a list to store human-readable date options date_options = [] # Iterate through the time slots until there are at least 3. current_time = start_singapore option_number = 1 iterator = 1 print("current time = " + str(current_time)) while len(available_time_slots) < 3: print(iterator) iterator+=1 # Check if the current time slot is available events_result = service.events().list(calendarId='primary', timeMin=current_time.isoformat(), timeMax=(current_time + datetime.timedelta(hours=1)).isoformat(), maxResults=1, singleEvents=True, orderBy='startTime').execute() events = events_result.get('items', []) # If no event is scheduled, the slot is available if not events and is_valid_timeslot(current_time): slot_start = current_time slot_end = current_time + datetime.timedelta(hours=1) # Add the human-readable date option date_options.append(f"Option {option_number}: {format_datetime(slot_start)} - {format_datetime(slot_end)}") available_time_slots.append({'start': slot_start.isoformat(), 'end': slot_end.isoformat()}) option_number+=1 current_time = get_next_weekday(current_time) # Convert the list of date options to a human-readable string date_options_str = "\n".join(date_options) # Return the available_time_slots JSON and the human-readable date options as a tuple result = { "date_options": date_options_str, "timeslot1_start": available_time_slots[0]['start'], "timeslot1_end": available_time_slots[0]['end'], "timeslot2_start": available_time_slots[1]['start'], "timeslot2_end": available_time_slots[1]['end'], "timeslot3_start": available_time_slots[2]['start'], "timeslot3_end": available_time_slots[2]['end'] } return result except HttpError as error: print('An error occurred: %s' % error) if __name__ == '__main__': retrieve_events() ``` ### Code Explanation - Create Event This code is responsible for creating events once the customer has decided which event to book. The API endpoint is called in **Step 7)** of the automation builder workflow. Please note it requires the use of credentials for the Google Calendar API as mentioned in the section above. Create Event ```python # Refer to the Python quickstart on how to setup the environment: # https://developers.google.com/calendar/quickstart/python # Change the scope to 'https://www.googleapis.com/auth/calendar' and delete any # stored credentials. from __future__ import print_function from flask import jsonify import datetime import os.path import config from google.auth.transport.requests import Request from google.oauth2.credentials import Credentials from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build from googleapiclient.errors import HttpError # If modifying these scopes, delete the file token.json. SCOPES = ['https://www.googleapis.com/auth/calendar'] def generate_request_id(customer_email): # Generate a unique requestId based on current date, time, and guest email current_time = datetime.datetime.now().strftime("%Y%m%d%H%M%S") request_id = f'{current_time}_{customer_email}' return request_id def create_event(timeslot_start, timeslot_end, customer_email): """Shows basic usage of the Google Calendar API. Prints the start and name of the next 10 events on the user's calendar. """ creds = None # The file token.json stores the user's access and refresh tokens, and is # created automatically when the authorization flow completes for the first # time. if os.path.exists('token.json'): creds = Credentials.from_authorized_user_file('token.json', SCOPES) # If there are no (valid) credentials available, let the user log in. if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file( 'google_api_creds.json', SCOPES) creds = flow.run_local_server(port=0) # Save the credentials for the next run with open('token.json', 'w') as token: token.write(creds.to_json()) try: service = build('calendar', 'v3', credentials=creds) request_id = generate_request_id(customer_email) event = { 'summary': '8x8 Customer Meeting', 'description': 'A chance to speak to a 8x8 support team expert.', 'start': { 'dateTime': timeslot_start, 'timeZone': 'Asia/Singapore', }, 'end': { 'dateTime': timeslot_end, 'timeZone': 'Asia/Singapore', }, 'reminders': { 'useDefault': False, 'overrides': [ {'method': 'email', 'minutes': 24 * 60}, {'method': 'popup', 'minutes': 10}, ], }, 'attendees': [ {'email': config.GOOGLE_ACCOUNT}, {'email': customer_email}, ], 'conferenceData': { 'conferenceSolution': { 'key': { "type":"addOn" }, 'name': '8x8' }, 'entryPoints': [ { 'entryPointType': 'video', 'uri': 'https://8x8.vc/8x8/'+request_id, # Replace with your custom conference link 'label': 'https://8x8.vc/8x8/'+request_id, } ] } } event_details = service.events().insert(calendarId='primary', body=event, conferenceDataVersion=1,sendUpdates="all").execute() return jsonify(event_details) except HttpError as error: print(str(error)) return jsonify({'error': str(error)}), 400 if __name__ == '__main__': create_event() ``` ### Google Calendar Organizational Use Use for an organization is beyond the scope of this tutorial. If you are trying to book to a Google Calendar for a user managed by an organization, then you may need to ask your IT team's admin for access to a service account with account-wide delegation [access](https://support.google.com/a/answer/162106?hl=en). --- ## Tutorial: Customer Survey via SMS ## Introduction In this tutorial, you'll learn how to create an SMS-based chatbot that prompts users with survey questions and sends their responses to Google Sheets. This process can also be adapted for other survey tools like Qualtrics or SurveyMonkey. The default tutorial includes three questions, but you can customize it with your own questions and modify the workflow as needed. Use this tutorial as a foundation to develop your own tailored customer surveys. ## Prerequisites Ensure you have: * An **8x8 account** with a subaccount setup. * An **8x8 Virtual Number** to use to send SMS that is already registered with 8x8. * Access to **Google Sheets** and its API (or another sheet/survey tool) * **Pipedream** account (or a similar API connector tool like Zapier, Make, or your own API server) If you do not have an 8x8 account or virtual number, reach out to your 8x8 account manager or email [cpaas_sales@8x8.com](mailto:cpaas_sales@8x8.com). ## Video Demo This is a accompanying video meant to show the SMS Customer Survey as a demo. ## Step 1: Setup the Automation Builder Workflow You can find an example Automation Workflow workflow below, which you can use to import into the Automation Builder UI in [Connect](https://connect.8x8.com/automation). While we will not explain the different components of the tutorial here, if you need a refresher please see our section on Automation Builder [Steps](triggers-and-steps-library) and [Triggers](triggers). > 📘 **Note** > > Please change the mobile number in the JSON below and the keywords as necessary. > > tutorial_sms_survey.json ```json { "definition": { "id": "75f9d264-8d6a-428d-989d-2093343b3f12", "name": "RS SMS Survey Demo", "version": 14, "steps": [ { "stepType": "Branch", "id": "branch_7588", "do": [], "nextStepId": null, "inputs": {}, "outputs": {}, "selectNextStep": { "sms_9310": "{{stringContains(data.payload.body, 'rssmssd', true)}}" } }, { "stepType": "SMS", "id": "sms_9310", "do": [], "nextStepId": "waitforreply_4914", "inputs": { "subAccountId": "InternalDemoCPaaS_8dD15_DemoNumber3", "source": "+6599999999", "destination": "{{data.payload.source}}", "text": "Welcome to the 8x8 Customer Satisfication Survey. You will be asked 3 questions regarding our 8x8 Products.\n\nQuestion 1: What 8x8 Products do you Use?" }, "outputs": {}, "selectNextStep": {} }, { "stepType": "WaitForReply", "id": "waitforreply_4914", "do": [], "nextStepId": null, "inputs": { "from": "{{data.payload.source}}", "channel": "sms", "timeout": "0.00:01:00" }, "outputs": { "waitforreply_4914_step_body": "{{step.reply.payload.body}}" }, "selectNextStep": { "sms_6631": "{{ step.reply != null }}", "sms_3283": "{{ step.reply == null }}" } }, { "stepType": "SMS", "id": "sms_6631", "do": [], "nextStepId": "waitforreply_3053", "inputs": { "subAccountId": "InternalDemoCPaaS_8dD15_DemoNumber3", "source": "+6599999999", "destination": "{{data.payload.source}}", "text": "Question 2: How would you rate your overall experience from a scale of 10 to 1? \n\n10 being the best experience and 1 being the worst experience." }, "outputs": {}, "selectNextStep": {} }, { "stepType": "SMS", "id": "sms_3283", "do": [], "nextStepId": null, "inputs": { "subAccountId": "InternalDemoCPaaS_8dD15_DemoNumber3", "source": "+6599999999", "destination": "{{data.payload.source}}", "text": "No responses in allotted time, ending the survey. Your responses have not been recorded." }, "outputs": {}, "selectNextStep": {} }, { "stepType": "WaitForReply", "id": "waitforreply_3053", "do": [], "nextStepId": null, "inputs": { "from": "{{data.payload.source}}", "channel": "sms", "timeout": "0.00:01:00" }, "outputs": { "waitforreply_3053_step_body": "{{step.reply.payload.body}}" }, "selectNextStep": { "sms_2464": "{{ step.reply != null }}", "sms_0587": "{{ step.reply == null }}" } }, { "stepType": "SMS", "id": "sms_2464", "do": [], "nextStepId": "waitforreply_8091", "inputs": { "subAccountId": "InternalDemoCPaaS_8dD15_DemoNumber3", "source": "+6599999999", "destination": "{{data.payload.source}}", "text": "Question 3: What are the major issues that you faced with 8x8 Products?" }, "outputs": {}, "selectNextStep": {} }, { "stepType": "SMS", "id": "sms_0587", "do": [], "nextStepId": null, "inputs": { "subAccountId": "InternalDemoCPaaS_8dD15_DemoNumber3", "source": "+6599999999", "destination": "{{data.payload.source}}", "text": "No responses in allotted time, ending the survey. Your responses have not been recorded." }, "outputs": {}, "selectNextStep": {} }, { "stepType": "WaitForReply", "id": "waitforreply_8091", "do": [], "nextStepId": null, "inputs": { "from": "{{data.payload.source}}", "channel": "sms", "timeout": "0.00:01:00" }, "outputs": { "waitforreply_8091_step_body": "{{step.reply.payload.body}}" }, "selectNextStep": { "sms_7820": "{{ step.reply != null }}", "sms_3235": "{{ step.reply == null }}" } }, { "stepType": "SMS", "id": "sms_7820", "do": [], "nextStepId": "httprequest_5514", "inputs": { "subAccountId": "InternalDemoCPaaS_8dD15_DemoNumber3", "source": "+6599999999", "destination": "{{data.payload.source}}", "text": "Thank you for your responses to our survey! Your responses are being recorded." }, "outputs": {}, "selectNextStep": {} }, { "stepType": "SMS", "id": "sms_3235", "do": [], "nextStepId": null, "inputs": { "subAccountId": "InternalDemoCPaaS_8dD15_DemoNumber3", "source": "+6583390627", "destination": "{{data.payload.source}}", "text": "No responses in allotted time, ending the survey. Your responses have not been recorded." }, "outputs": {}, "selectNextStep": {} }, { "stepType": "HttpRequest", "id": "httprequest_5514", "do": [], "nextStepId": null, "inputs": { "headers": { "content-Type": "application/json" }, "method": "POST", "url": "https://eo4yuu59vzo9m6.m.pipedream.net", "parameters": {}, "body": { "response1": "{{data.waitforreply_4914_step_body}}", "response2": "{{data.waitforreply_3053_step_body}}", "response3": "{{data.waitforreply_8091_step_body}}", "mobileNumber": "{{data.payload.source}}" }, "timeoutSeconds": 30 }, "outputs": {}, "selectNextStep": {} } ] }, "subAccountId": "InternalDemoCPaaS_8dD15_DemoNumber3", "trigger": "inbound_sms", "status": "enabled" } ``` Once the workflow is imported, it should appear within Automation Builder similar to the one below. ![Workflow Part 1 of 2](../images/011b704-image.png)Workflow Part 1 of 2 ![Workflow Part 2 of 2.](../images/c624929-image.png)Workflow Part 2 of 2. It is comprised of **Send SMS Step, Branch Step, Wait for Reply Step** and a single **HTTP Request Step**. If you choose to modify the flow you may also need to modify the request body to Pipedream at the end depending on the questions that you ask. ## Step 2: Setup Google Sheet Setup a Google Sheet with the following columns on your Google Account which we will use later within Pipedream to populate. ![image](../images/2f3913c-image.png) Here is an example table that you can be copy/pasted to your Google Sheet. | Mobile Number | Response 1:What 8x8 Products do you Use? | Response 2: How would you rate your overall experience from a scale of 10 to 1? | Response 3: What are the major issues that you faced with 8x8 Products? | | --- | --- | --- | --- | | | | | | ## Step 3: Setup Pipedream Setup a new workflow with an **HTTP Trigger** followed by a **Google Sheets: Add Single Row Step** ![image](../images/b4333bb-image.png) The **HTTP Trigger** should have these following configurations: ![image](../images/cc24cff-image.png) Within the **HTTP Trigger,** go to **Generate Test Event** and use the following JSON as the Test Event's input. This will allow us to correctly populate the values for the following Google Sheet step. ![image](../images/e2dff8a-Screenshot_2024-06-20_at_5.28.36_PM.png) ```json { "response1": "WhatsApp, SMS", "response2": "10", "response3": "No Issues!", "mobileNumber": "+6599999999" } ``` The **Google Sheets Step** should have the following configuration. ![image](../images/5ab2223-Screenshot_2024-06-20_at_5.30.40_PM.png) Within **Pipedream**, you should be able to see the HTTP responses sent by Automation Builder which may be useful in case any debugging is required. ![image](../images/f8264b4-Screenshot_2024-06-21_at_10.11.34_AM.png) ## Step 4: Send SMS After setting up the above, you should be able to send a SMS message to your Virtual Number tied to 8x8 and receive responses back prompting you to complete the survey as outlined below. ![image](../images/92a5216-image.png) This should result in a row being added to your Google Sheet with the response. ![image](../images/2f7c9aa-image.png) ## Conclusion While we use **Google Sheets** in this tutorial, the same idea can be extended to a dedicated Customer Survey software like **Qualtrics, SurveyMonkey, Alchemer**, **etc**. Similarly while we used **Pipedream** for this tutorial, another tool that offers similar HTTP Trigger and Google Sheet Integration capabilities can also be used in it's place. **Expanded Explanation** * **Increased Response Rate:** Reduces friction, making it easier for users to participate. * **Higher Engagement Levels:** Uses interactive features to keep respondents interested. * **Enhanced Reach and Accessibility:** Broadens audience reach due to SMS near ubiquitous reach. * **Reduced Technical Issues:** Minimizes problems like slow loading and compatibility issues. We encourage to take this tutorial as a template and try it out with your own systems to craft a survey using automation builder. --- ## Mobile Verification ## Tutorial: Learn how to use API for Mobile Verification Code Generation & Validation ### Introduction One of the most common use cases for SMS is about conveying a one-time password (OTP) to an end user's mobile phone. 8x8 offers a dedicated API service to make SMS OTP verification process easier for you. In this tutorial, we are going to cover the following two methods: [Mobile Verification - Code Generation](/connect/reference/verify-request-v2) and [Mobile Verification - Code Validation](/connect/reference/code-validation-v2). As their names put it, they are about generating then sending a code to a user and then verifying it. If you follow the different steps of this tutorial, you will get to generate a code and verify it directly from your command line utility using a simple curl command. ### Prerequisites * Command line interface compatible with CURL * 8x8 CPaaS account * apiKey (Bearer token) * 8x8 CPaaS subaccountid * Destination phone number * SMS Body * Brand name (SMS SenderID) --- ### Account and credentials *You will need to sign up to use the API. The following steps will guide you through this process and highlight the information to keep aside.* #### I. Signing-up 1. Head to [8x8 Connect sign-up page](https://connect.8x8.com/login/signup) 2. Enter your email and follow the instructions to define your password and finalize your account (by default, API password and account password are the same, you can modify this from your account settings) 3. Confirm your email address by clicking on the validation link you received in the activation email to activate your account. ![Signup 8x8 connect](../images/45380f8-Signup_8x8_connect.png "Signup 8x8 connect.png") #### II. Finding your apiKey token (for API authentication) 1. Head to [8x8 Connect Login Page](https://connect.8x8.com). 2. Click on LOG IN. 3. Enter your email address and password to get access to your account dashboard. 4. Head over to the **side menu > API keys** section 5. Create an API key if empty and then keep the API Key value, here: `5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg` ![image](../images/441a309-API_2.png "API 2.png") #### III. Identifying your Subaccountid 1. Head over to the pricing section and use the subaccountid list to retrieve the `subaccountid` that you want to use 2. By default, your account comes with only one `subaccountid` for your high-quality service. It is designated by your `accountid` and the suffix `_hq`. 3. Note down this value, you will need it later. 4. In that example, the `subaccountid` is `acme_corp` ![image](../images/9c38989-API_3.png "API 3.png") --- ### Part 1: Generating and sending a code using the Mobile Verification API - Code Generation method The 8x8 Mobile Verification - Code Generation method expects requests sent by developers to respect a specific format. In the following parts, we are going to go over the different elements of the request: * the URL format * the authentication * the data payload. At the end of the section, we will generate a curl command to generate an SMS containing a one-time password directly from the command line. #### I. Preparing the request URL ##### Remarks * We are going to send a POST request to the Mobile Verification API - Code Generation endpoint. * As detailed in the [documentation](/connect/reference/verification-api), the URL is defined by the following pattern: `https://verify.8x8.com/api/v2/subaccounts/{subAccountId}/sessions` ##### Tutorial URL * In order to create the URL to use, we are going to replace `{subaccountid}` in the pattern above by `acme_corp`, the subaccountid that we are using in this tutorial * In that example, the URL that we are going to send the request to is: `https://verify.8x8.com/api/v2/subaccounts/acme_corp/sessions` ##### Platform Deployment Region * To ensure the use of the correct platform deployment region, it is necessary to modify the base URL to correspond with the provisioned region of your account. Refer to the table below for the appropriate base URL associated with each platform region: | URL | Region | | --- | --- | | [https://verify.8x8.com](https://verify.8x8.com) | Asia Pacific (default) | | [https://verify.us.8x8.com](https://verify.us.8x8.com) | North America | | [https://verify.8x8.uk](https://verify.8x8.uk) | Europe | | [https://verify.8x8.id](https://verify.8x8.id) | Indonesia | * For more information on platform deployment regions, please visit the following [page](/connect/docs/platform-deployment-regions). ##### ##### curl * In curl, we will have to indicate that we want to do a POST request to this URL by using the following command: ```bash curl -i -X "POST" https://verify.8x8.com/api/v2/subaccounts/acme_corp/sessions ``` * The `-i` flag (case-sensitive) will allow to print the request and response body and headers * The `-X` flag is used to specify the HTTP method to use for the request (POST, HEAD, PUT, GET, DELETE...) - Here we are using a POST request. #### II. Preparing the request authentication #### Remarks * As explained in the [docs](/connect/docs/authentication), the API authentication uses an apiKey bearer token method. #### Tutorial authentication * In this tutorial, the apiKey for our account is `5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg`. #### curl * In a curl request, bearer tokens must be passed as a header like so: `-H "Authorization: Bearer {token}"` * We just have to replace our `{token}` placeholder by our apiKey * The authorization header will then look like that: `-H "Authorization: Bearer 5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg"` #### III. Preparing the request data payload * The API expects to receive a structured request containing the SMS data in a specific format. * As detailed in the [documentation](/sms/API-Reference/mobile-verification-api/send-otp), the data that we have to submit should be a JSON object containing at least a **destination** field. Let's keep it simple and add a brand name as well as a country code since our destination phone number is in the national format (as opposed to the international format): *Example of JSON object:* ```json { "destination": "98765432", "country": "SG", } ``` #### curl * In curl, we will transmit the JSON data inline and indicate that the data payload is in JSON format using the following commands: ```bash -H "Content-Type: application/json" -d $'{ "destination": "98765432", "country": "SG" }' ``` #### IV. Putting it together and posting the curl request * If we wrap up all the elements prepared in the steps above, we should put together the 3 elements of our request: **URL + Authentication + Data Payload** * To send the API request to 8x8 Mobile Verification - Code Generation endpoint we should use the following command in our command line utility: ```bash curl -i -X "POST" https://verify.8x8.com/api/v2/subaccounts/acme_corp/sessions -H "Authorization: Bearer 5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg" -H "Content-Type: application/json" -d $'{ "destination": "6598765432", "country": "SG"}' ``` * And that’s it! Here is the result on the destination device: ![image](../images/748c827-Iv_Curl.png "Iv Curl.png") #### V. API response * When sending the curl command above from your command line utility, you notice that the 8x8 API sends back a response that shows up in your terminal, for example in this tutorial: ```json { "sessionID": "7c1137e8fb1ceb11827c00155dc319db", "verifyUri": "/api/v2/subaccounts/acme_corp/sessions/", "destination": 6598765432, "status": "WAITING", "attempt": 0, "expiresAt": "2020-11-02T11:14:44.70Z", "retryAfter": "2020-11-02T11:09:54.70Z" } ``` * The API response is used to provide feedback about the expected result of the API request (sending an SMS) and various additional information. * If we take some time to analyze the different elements there, we can identify the following: * **uid**: this is the unique identifier set by the API to identify this request. It will be used to check its status and the codes provided. * **resourceUri**: it indicates the uniform resource identifier within 8x8 API * **destination**: this is the phone number to which the SMS was sent. * **status**: the status of the Mobile Verification request * **attempt**: this counter shows how many unsuccessful attempts have been made to verify this code * **expiresAt**: this is the timestamp that indicates when the code will expire * **retryAfter**: this timestamp indicates when the system will allow sending another SMS if a new request is sent * For more information, check the dedicated section of the [API documentation](/sms/API-Reference/mobile-verification-api/send-otp), in the "Response" section ### Part 2: Verifying a code received using the Mobile Verification API - Code Validation method So our user received successfully his SMS containing the code required to verify his mobile phone number (*see [Part 1 - IV]*). Here below are the elements from the part 1 that are going to be used in this part 2: * **apiKey** = `5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg` * **Subaccountid** = *acme_corp* * **sessionId** = *7c1137e8fb1ceb11827c00155dc319db* * **code** = *5612* #### Request URL According to the [documentation](/sms/API-Reference/mobile-verification-api/verify-otp), the 8x8 Mobile Verification - Code Validation method is much simpler to use: it simply expects a **GET request** sent to a URL built using 2 different parameters: * **the subaccountid:** *acme_corp* * **the sessionID returned by the API during part 1:** *7c1137e8fb1ceb11827c00155dc319db* ➡ the URL where to send the GET request should follow this structure: `https://verify.8x8.com/api/v2/subaccounts/{subAccountId}/sessions/{sessionId}` In our example, we just have to use the parameters' values listed above to compose our URL: `https://verify.8x8.com/api/v2/subaccounts/acme_corp/sessions/7c1137e8fb1ceb11827c00155dc319` #### curl In curl, we will have to indicate that we want to do a POST request to this URL by using the following command: ```bash curl -i -X "GET" https://verify.8x8.com/api/v2/subaccounts/acme_corp/sessions/7c1137e8fb1ceb11827c00155dc319 ``` #### II. Preparing the request authentication #### Remarks * As explained in the [docs](/connect/docs/authentication), the API authentication uses an apiKey bearer token method. #### Tutorial authentication * In this tutorial, the apiKey for our account is `5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg`. #### curl * In a curl request, bearer tokens must be passed as a header like so: `-H "Authorization: Bearer {token}"` * We just have to replace our `{token}` placeholder by our apiKey * The authorization header will then look like that: `-H "Authorization: Bearer 5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg"` #### III. Preparing the request data payload #### Remarks * Contrary to the POST request in part 1, the data here is passed using a query string: the parameters are appended directly in the URL where we send the GET request * This method can be used to consult the status of a request without actually verifying it: if no code value is appended in the GET request URL, then the API will respond with the status of the request without changing it. * To perform the validation action, a code value must be appended in the GET request URL: if the code is the right one and has not expired, the status of the request will be changed to VERIFIED. If the code is not the right one, the attempt counter will be incremented. #### Appending the code in the query string for our code validation * Our code value is 5612 * A query string is appended at the end of the URL using `?` as separator and then `{parameter_name}={parameter_value}` ➡ We will have to append the following query string to our URL: `?code=5612` #### IV. Putting it together and posting the curl request * If we wrap up all the elements prepared in the steps above, we should put together the 3 elements of our request: **URL + Query string appended + Authentication** * To send the API request to 8x8 Mobile Verification - Code Validation endpoint we should use the following command in our command line utility: ```bash curl -X GET 'https://verify.8x8.com/api/v2/subaccounts/acme_corp/sessions/7c1137e8fb1ceb11827c00155dc319?code=5612' -H "Authorization: Bearer 5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg" ``` * And that’s it! Here is the response from our terminal: ```json { "uid": "7c1137e8fb1ceb11827c00155dc319", "resourceUri": "/api/v2/subaccounts/acme_corp/sessions/7c1137e8fb1ceb11827c00155dc319", "destination": 6598765432, "status": "VERIFIED", "attempt": 0, "expiresAt": "2020-11-02T13:19:56.70Z", "nextSmsAfter": "2020-11-02T13:14:44.70Z" } ``` You can see that the status of the request is now verified: the code provided was the right one which means that your user provided a legitimate mobile phone number where he received the code generated by the API, you can let him through! --- ## Tutorial: Opt Out Keyword (SMS) for Third-Party System ## Overview This tutorial will show you how to use Automation Builder to build an **opt-out flow** for SMS. **This workflow in automation builder will perform these steps:** 1. Listen for a keyword sent via SMS to a specific number attached to your subaccount 2. Send a HTTP request to your server with information regarding the customer’s phone number and the keyword they specified so that your server can perform opt-out logic 3. Send a follow up SMS to let the customer know they have opted out. 8x8 has already pre-built an Automation Builder template which allows you to add a keyword which will allow customers to opt out of further communications. We will use this template as the basis of the tutorial. ## Pre-Built Template First go to the Automation Builder page ([https://connect.8x8.com/automation](https://connect.8x8.com/automation)) and select the blue button for "Create Custom Workflow" ![image](../images/d1aab65-image.png) ## Opt-Out Template From the pop up in the next page, select the "Opt-Out" template to use as our base. ![image](../images/a6beedd-image.png) ## Building the Workflow In the next page the workflow should be pre-populated with steps, click on the "Trigger" step which is the first step in the flow. Select the subaccount which has the shortcode number that you wish to associate with this opt-out flow. Once it is selected click "Update" to save it to the step. ![image](../images/03bccf8-image.png) ## Deciding the Keyword The **keyword** is controlled by the "Branch" Step in red, in this example it is set to **STOP** but you can modify this if you wish to change the condition to trigger the opt out. ![Opt Out Flow Part 1 of 2](../images/83371bc-image.png) ![Opt Out Flow Part 2 of 2](../images/b1fcb73-image.png) > 📘 **The branch step has a limit of 5 choices in the Automation Builder UI. You can create additional workflows with additional choices if you need them at the top level of your menu. Another option is to look into the Automation Builder API to build this workflow instead which can support more than 5 choices.** > > ## Add Contact to Opt Out Group Once that is done, you can move on to modifying the HTTP Request itself. In the examples the URL and the headers should be substituted for the URL and headers (if required) of your server endpoint instead. ![image](../images/23bf390-image.png) | Field | Value | | --- | --- | | Step Name | Any Value | | URL | POST to your URL endpoint to handle the request. | | Header - Content Type | application/json | | Header - AuthorisationNote: Your API key can be obtained from the API Keys section of the Connect Dashboard. | Bearer | | Request Body (Example, this will include the phone number that sent the SMS message to your 8x8 number from {{data.payload.source}}. The text that the user inputted will be in {{data.payload.content.text}}.The example will send a JSON response body to your API endpoint that will be similar to.{"msisdn": "+6512345678","subaccount": "SAMPLE_SUBACCOUNT"} | {"msisdn": "{{data.payload.source}}","subaccount": "{{data.payload.content.text}}"} | ## Opt Out Message You can replace the message with a custom message to your users. Make sure to again select the correct subaccount for your short code that you want to send the SMS from. ![image](../images/fdb2a07-image.png) | Field | Example Value | | --- | --- | | Step Name | Any Value | | Please select a new subaccount | Your 8x8 Subaccount | | Sender ID | Your 8x8 Sender ID | | Destination | {{data.payload.source}} | | Message | We're sorry to see you go, but confirm you have been unsubscribed and will no longer receive SMS from us. | After saving these changes, the opt out flow can be enabled by enabling the button at the top left and saving. It will activate immediately and any message that contains the keyword will result in a user being added to the opt-out group. ![image(../images/3fd8cd6-image.png) ## Testing Once the automation workflow is in place, you can test it by sending the opt-out keyword via SMS to the phone number attached to your subaccount. Note you will need to build a URL endpoint to accept the HTTP request that will come from the Automation Builder workflow which is outside the scope of this guide. ## Removing from Opt out list You can reverse this logic to implement something like a resubscribe or an opt-in so that customers can resubscribed if needed. --- ## Tutorial: Opt Out Keyword (SMS) for Third-Party System(Docs) ## Overview This tutorial will show you how to use Automation Builder to build an **opt-out flow** for SMS. **This workflow in automation builder will perform these steps:** 1. Listen for a keyword sent via SMS to a specific number attached to your subaccount 2. Send a HTTP request to your server with information regarding the customer’s phone number and the keyword they specified so that your server can perform opt-out logic 3. Send a follow up SMS to let the customer know they have opted out. 8x8 has already pre-built an Automation Builder template which allows you to add a keyword which will allow customers to opt out of further communications. We will use this template as the basis of the tutorial. ## Pre-Built Template First go to the Automation Builder page ([https://connect.8x8.com/automation](https://connect.8x8.com/automation)) and select the blue button for "Create Custom Workflow" ![image](../images/d1aab65-image.png) ## Opt-Out Template From the pop up in the next page, select the "Opt-Out" template to use as our base. ![image](../images/a6beedd-image.png) ## Building the Workflow In the next page the workflow should be pre-populated with steps, click on the "Trigger" step which is the first step in the flow. Select the subaccount which has the shortcode number that you wish to associate with this opt-out flow. Once it is selected click "Update" to save it to the step. ![image](../images/03bccf8-image.png) ## Deciding the Keyword The **keyword** is controlled by the "Branch" Step in red, in this example it is set to **STOP** but you can modify this if you wish to change the condition to trigger the opt out. ![Opt Out Flow Part 1 of 2](../images/83371bc-image.png) ![Opt Out Flow Part 2 of 2](../images/b1fcb73-image.png) > 📘 **The branch step has a limit of 5 choices in the Automation Builder UI. You can create additional workflows with additional choices if you need them at the top level of your menu. Another option is to look into the Automation Builder API to build this workflow instead which can support more than 5 choices.** > > ## Add Contact to Opt Out Group Once that is done, you can move on to modifying the HTTP Request itself. In the examples the URL and the headers should be substituted for the URL and headers (if required) of your server endpoint instead. ![image](../images/23bf390-image.png) | Field | Value | | --- | --- | | Step Name | Any Value | | URL | POST to your URL endpoint to handle the request. | | Header - Content Type | application/json | | Header - AuthorisationNote: Your API key can be obtained from the API Keys section of the Connect Dashboard. | Bearer | | Request Body (Example, this will include the phone number that sent the SMS message to your 8x8 number from {{data.payload.source}}. The text that the user inputted will be in {{data.payload.content.text}}.The example will send a JSON response body to your API endpoint that will be similar to.{"msisdn": "+6512345678","subaccount": "SAMPLE_SUBACCOUNT"} | {"msisdn": "{{data.payload.source}}","subaccount": "{{data.payload.content.text}}"} | ## Opt Out Message You can replace the message with a custom message to your users. Make sure to again select the correct subaccount for your short code that you want to send the SMS from. ![image](../images/fdb2a07-image.png) | Field | Example Value | | --- | --- | | Step Name | Any Value | | Please select a new subaccount | Your 8x8 Subaccount | | Sender ID | Your 8x8 Sender ID | | Destination | {{data.payload.source}} | | Message | We're sorry to see you go, but confirm you have been unsubscribed and will no longer receive SMS from us. | After saving these changes, the opt out flow can be enabled by enabling the button at the top left and saving. It will activate immediately and any message that contains the keyword will result in a user being added to the opt-out group. ![image](../images/3fd8cd6-image.png) ## Testing Once the automation workflow is in place, you can test it by sending the opt-out keyword via SMS to the phone number attached to your subaccount. Note you will need to build a URL endpoint to accept the HTTP request that will come from the Automation Builder workflow which is outside the scope of this guide. ## Removing from Opt out list You can reverse this logic to implement something like a resubscribe or an opt-in so that customers can resubscribed if needed. --- ## Single SMS ## Tutorial: Learn how to use API for sending single SMS ### **Introduction** 8x8 offers different API methods that allow you to send SMS programmatically. In this tutorial, we are going to cover the simplest method: [Send SMS](/connect/reference/send-sms-single) It is used to send SMS one by one. We also offer bulk methods that allow sending multiple SMS in one command - for more information, check the following methods in the documentation: [Send Many SMS](/connect/reference/send-many-sms) If you follow the different steps of this tutorial, you will get to send an SMS directly from your command line utility using a simple curl command. ### Index * Learn how to use the method for sending single SMS * Prerequisites * Account and credentials * Signing-up * Finding your apiKey bearer token (for API authentication) * Identifying your Subaccountid * API request * Preparing the URL * Preparing the authentication * Preparing the data payload * Putting it together and posting the curl request * Going further * API response * API errors In this tutorial, we are going to send the text “Bob, a special present for you from Santa” to the mobile phone number 12345678 registered on a Singaporean network (+65) using curl. For this, we are going to use an 8x8 account created with our email [amazingdeveloperr@gmail.com](mailto:amazingdeveloperr@gmail.com) . We are going to use our subaccountid `acme_corp`. The apiKey for our account is `5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg`. ### Prerequisites * Command line interface compatible with CURL * 8x8 CPaaS account * apiKey (Bearer token) * 8x8 CPaaS subaccountid * Destination phone number * SMS Body --- ### Account and credentials *You will need to sign-up to use the API. The following steps will guide you through this process and highlight the information to keep aside.* #### I. Signing-up 1. Head to [8x8 Connect sign-up page](https://connect.8x8.com/login/signup) 2. Enter your email and follow the instructions to define your password and finalize your account (by default, API password and account password are the same, you can modify this from your account settings) 3. Confirm your email address by clicking on the validation link you received in the activation email to activate your account. ![Signup 8x8 connect](../images/1678cef-Signup_8x8_connect.png "Signup 8x8 connect.png") #### II. Finding your apiKey bearer token (for API authentication) 1. Head to [8x8 Connect Login Page](https://connect.8x8.com). 2. Click on LOG IN. 3. Enter your email address and password to get access to your account dashboard. 4. Head over to the **side menu > API keys** section 5. Create an API key if empty and then keep the API Key value, here: `5DhZxZR*************9pYmg` ![image](../images/e168c88-API_2.png "API 2.png") #### III. Identifying your Subaccountid 1. Head over to the pricing section and use the subaccountid list to retrieve the `subaccountid` that you want to use 2. By default, your account comes with only one `subaccountid` for your high-quality service. It is designated by your `accountid` and the suffix `_hq`. 3. Note down this value, you will need it later. 4. In that example, the `subaccountid` is `acme_corp` ![image](../images/2c35b4c-API_3.png "API 3.png") --- ### API Request The 8x8 SMS single method expects requests sent by developers to respect a specific format. In the following parts, we are going to go over the different elements of the request: * the URL format * the authentication * the data payload. At the end of the section, we will generate a curl command to send an SMS directly from the command line. #### I. Preparing the request URL ##### Remarks * We are going to send a POST request to the 8x8 API single URL endpoint. * As detailed in the [Send SMS](/connect/reference/send-sms-single) , the URL is defined by the following pattern: `https://sms.8x8.com/api/v1/subaccounts/{subAccountId}/messages` ##### Tutorial URL * In order to create the URL to use, we are going to replace `{subaccountid}` in the pattern above by `acme_corp`, the subaccountid that we are using in this tutorial * In that example, the URL that we are going to send the request to is: `https://sms.8x8.com/api/v1/subaccounts/acme_corp/messages` ##### Platform Deployment Region * To ensure the use of the correct platform deployment region, it is necessary to modify the base URL to correspond with the provisioned region of your account. Refer to the table below for the appropriate base URL associated with each platform region: | URL | Region | |--------------------------------------------------|------------------------| | [https://sms.8x8.com](https://sms.8x8.com) | Asia Pacific (default) | | [https://sms.us.8x8.com](https://sms.us.8x8.com) | North America | | [https://sms.8x8.uk](https://sms.8x8.uk) | Europe | | [https://sms.8x8.id](https://sms.8x8.id) | Indonesia | * For more information on platform deployment regions, please visit the following [page](/connect/docs/platform-deployment-regions). ##### curl * In curl, we will have to indicate that we want to do a POST request to this URL by using the following command: ```bash curl -X "POST" https://sms.8x8.com/api/v1/subaccounts/acme_corp/messages ``` #### II. Preparing the request authentication #### Remarks * As explained in the [Authentication Docs](/connect/docs/authentication), the API authentication uses an apiKey bearer token method. #### Tutorial authentication * In this tutorial, the apiKey for our account is `5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg`. #### curl * In a curl request, bearer tokens must be passed as a header like so: `-H "Authorization: Bearer {token}"` * We just have to replace our `{token}` placeholder by our apiKey * The authorization header will then look like that: `-H "Authorization: Bearer 5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg"` #### III. Preparing the request data payload * The API expects to receive a structured request containing the SMS data in a specific format. * As detailed in the documentation, the data that we have to submit should be a JSON object structured as follows: ![image](../images/8c2ed20-JSON_Object_Properties.png "JSON Object Properties.png") #### Tutorial request data payload * The API expects to receive a structured request containing the SMS details and parameters. The format of the request is JSON and it can accept both optional and required parameters. * For simplicity sake, we are going to use only the most important of the parameters (the others are detailed in the documentation): * **Source**: * this parameter defines the SMS SenderID, let’s use `"Acme Corp"` 😎 * **Destination**: * this is the phone number that we want to reach. As mentioned in the introduction, we want to send a message to 12345678 and it is a phone number registered in Singapore, which uses the international prefix +65. * For the destination parameter, we are going to use the value `"+6512345678"` * **Text**: * This is the content of the message. * To invoke a festive season feeling, let’s use the value: `“Bob, a special present for you from Santa.”` * **Encoding**: * This parameter tells the destination handset which encoding to use to display the SMS. Our text only contains GSM7bit characters which is the most standard encoding but for the sake of safety let’s use `"AUTO"` * it means that the API will automatically detect the characters used in the text and select the best encoding. * It allows preventing the message from showing up as “ ⃞ ⃞ ⃞ ⃞, ⃞ ⃞ ⃞ ⃞” in the case where we would have used special characters or another alphabet. * Our final JSON object that we are going to send as the request data payload is then: ```json { "source": "Acme Corp", "destination": "+6512345678", "text": "Bob, a special present for you from Santa", "encoding": "AUTO" } ``` #### curl * In curl, we will transmit the JSON data inline and indicate that the data payload is in JSON format using the following commands: ```bash -H "Content-Type: application/json" -d $'{ "source": "Acme Corp", "destination": "+6512345678", "text": "Bob, a special present for you from Santa", "encoding": "AUTO" }' ``` #### IV. Putting it together and posting the curl request * If we wrap up all the elements prepared in the steps above, we should put together the 3 elements of our request: **URL + Authentication + Data Payload** * To send the API request to the SMS API endpoint with our message we should use the following command in our command line utility: ```bash curl -X "POST" https://sms.8x8.com/api/v1/subaccounts/acme_corp/messages \ -H "Authorization: Bearer 5DhZxZRILVPKjXuFWsd7QGZ**********31n19pYmg" \ -H "Content-Type: application/json" \ -d $'{ "source": "Acme Corp", "destination": "+6512345678", "text": "Bob, a special present for you from Santa", "encoding": "AUTO" }' ``` * And that’s it! Here is the result: ![437](../images/db2629f-Final_Image.png "Final Image.png") #### Going further #### I. API response * When sending the curl command above from your command line utility, you notice that the API sends back a response that shows up in your terminal, for example in this tutorial: ```json { "umid":"f9eaeb51-24fd-e611-813c-06ed3428fe67", "clientMessageId":null, "destination":"6512345678", "encoding": "GSM7", "status":{ "code":"QUEUED", "description":"SMS is accepted and queued for processing" } } ``` * The API response is used to provide feedback about the expected result of the API request (sending an SMS) and various additional information. * If we take some time to analyze the different elements there, we can identify the following: * **umid**: it stands for unique message id, it is the unique id associated with this SMS by the 8x8 CPaaS platform. * **clientmessageid**: here, it is null because no value has been specified in the request but you have the possibility to attribute your own custom ids to your messages. * **destination**: this is the phone number to which the SMS was sent. * **encoding**: this is the encoding used to send the message, it depends on the character set to use for the content. Here GSM7 is the standard when no UNICODE character is required. * **status**: this array contains 2 elements: the status of the message and the description of this status * For more information, check the dedicated section of the [Send SMS](/connect/reference/send-sms-single), "Response" section in the right panel. #### II. API errors * A developer World without error would not be funny! * If ever you send malformed requests to the API, it will let you know by using some specific error codes in the response. * You can find the different codes and their meanings in the [SMS Delivery receipts error codes](/connect/reference/delivery-receipts-error-codes) for delivery related errors as well as [API Error codes](/connect/reference/api-error-codes) for error codes that apply to all 8x8 CPaaS APIs. --- ## SMS Engage ## Introduction 8x8 offers API methods that allow you to send SMS Engage programmatically. In this tutorial, we will cover how to send a single SMS Engage: [Send SMS Engage survey](/connect/reference/survey-send) and multiple SMS Engage: [Send SMS Engage surveys as batch](/connect/reference/survey-send-many) in one command. --- ## Video Demo This video below will take you through a demo of SMS Engage including: sending a survey via API, filling out a survey and viewing the responses on the Connect Dashboard. ## Prerequisites * **8x8 Account** with an SMS Engage form created * If you do not have an SMS engage form, see the **SMS Engage Form** section below on how to obtain one. * **8x8 API Key** * Please see [this](developer-tools) page on how to create API Keys if you do not have an existing one. --- ## SMS Engage Form You must have at least one SMS Engage form created for you by 8x8. SMS Engage forms are created based on your use-cases. Once they are created, 8x8 will provide you a **surveyId** and \*_url_ac\*. * Please note that the variable url is your default SMS Engage link which 8x8 has set up for you (e.g. [http://smstoweb.net?sid=1234](http://smstoweb.net?sid=1234)). The url is shortened once the message has been sent. * The shortened url is always **21 characters** in length. * If you want to get the data via **Webhooks**, simply provide us a specific url where we will post the data. * For more information on **how to create an SMS engage form** please contact your account manager or send an email to [8x8 Support](mailto:cpaas-support@8x8.com). ![image](../images/44b0744-sub-account_sliders_hq.png "sub-account_sliders_hq.png") --- ## Send Single SMS Engage Survey The 8x8 SMS Engage survey method expects requests sent by developers to respect a specific format. In the following section, we are going to go over the different elements of the request: * URL format * Authentication * Data payload At the end of the section, we will generate a cURL command to send an SMS Engage directly from the command line. ### Request URL * As detailed in the [Send SMS Engage survey](/connect/reference/survey-send) , the URL is defined by the following pattern: `https://sms.8x8.com/api/v1/subaccounts/{subAccountId}/surveys/{surveyId}/messages` * The **{subAccountID}** should be replaced by your own 8x8 Subaccount ID and the **{surveyId}** should be replaced by your Survey ID in a request. ### Authentication Use your **API Key** in the **Authorization** header of your HTTP request, this is denoted with the variable below. ```bash curl --location 'https://sms.8x8.com/api/v1/subaccounts//surveys//messages' \ --header 'Content-Type: application/json' \ --header 'Authorization: ' \ ``` ### Data payload The API expects to receive a structured request containing the SMS data in a specific format. As detailed in the documentation, the data that we have to submit should be a JSON object. To send a **single SMS Engage** message here is an example of a data payload: ```json { "destination": "", "templateBody": "Hello {{firstName}}, your order is {{order_nr}}. {{url}} Please check and confirm if your scheduled delivery time is ok.", "templateVariables": { "firstName": "James", "order_nr": "ABC1000" }, "source": "" } ``` The JSON parameters are explained in the table below: | Parameter | Description | Example Value | | --- | --- | --- | | destination | Mobile Number in international format | +6512345678 | | templateBody | Template of message to user. It should have the variable **{{url}}**, this variable will be substituted in the SMS by the link set for you by 8x8. | Hello {{firstName}}, your order is {{order_nr}}. {{url}} Please check and confirm if your scheduled delivery time is ok. | | templateVariables | Variables or pre-defined fields used inside the templateBody. Your url should have the value or link set for you by 8x8. | "templateVariables": {"firstName": "James","order_nr": "ABC1000"}, | | Source | The Sender ID or Virtual Number to send the SMS from. | Acme | ### Sending the API Request Now that the API request is setup correctly you need, let’s try this by using cURL. If you are running **Mac OS**, **cURL** is already installed. Just run the **Terminal** app (Located under Applications->Utilities). For **Windows** based machines click [here](https://developer.zendesk.com/documentation/api-basics/getting-started/installing-and-using-curl/) on how to install curl. Alternatively you can use **Postman** if you are familiar with the tool as well to send this request. For **cURL** the command should look as follows, which can also be adapted into Postman. ```bash curl --location 'https://sms.8x8.com/api/v1/subaccounts//surveys//messages' \ --header 'Content-Type: application/json' \ --header 'Authorization: ' \ --data '{ "destination": "", "templateBody": "Hello {{firstName}}, your order is {{order_nr}}. {{url}} Please check and confirm if your scheduled delivery time is ok.", "templateVariables": { "firstName": "James", "order_nr": "ABC1000" }, "source": "8x8" }' ``` Remember to replace the **,** and values above. The **{{url}}** value does not need to be included in the te**mplateVariables** and will be automatically substituted by 8x8. This should result in a SMS being sent similar to the one below with the URL replacing the {{url}} parameter's place in the SMS Body. ![image](../images/6f385ec-image.png) A successful request should return an 200 OK HTTP Response Code and the Response Body should appear as below: ```json { "umid": "", "clientMessageId": null, "destination": "", "encoding": "GSM7", "status": { "code": "QUEUED", "description": "SMS is accepted and queued for processing" } } ``` Note that **mobileNumber** and **umid's** values will be replaced with your request's unique values. ## Send Batch SMS Engage Survey Now that we have covered how to send a Single SMS Engage Survey, there may also be times that it is appropriate to send multiple SMS Engage Surveys in a single API request. For those use cases, we can use the [SMS Engage Batch API Endpoint](survey-send-many). In the following section, we are going to go over the different elements of the request: * URL format * Authentication * Data payload At the end of the section, we will generate a cURL command to send an SMS Engage directly from the command line. ### Request URL * As detailed in the [Send SMS Engage survey](/connect/reference/survey-send) , the URL is defined by the following pattern: `https://sms.8x8.com/api/v1/subaccounts/{subAccountId}/surveys/{surveyId}/messages/batch` * The **{subAccountID}** should be replaced by your own 8x8 Subaccount ID and the **{surveyId}** should be replaced by your Survey ID in a request. ### Authentication Use your **API Key** in the **Authorization** header of your HTTP request, this is denoted with the variable below. ```bash curl --location 'https://sms.8x8.com/api/v1/subaccounts//surveys//messages/batch' \ --header 'Content-Type: application/json' \ --header 'Authorization: ' \ ``` ### Data payload The API expects to receive a structured request containing the SMS data in a specific format. As detailed in the documentation, the data that we have to submit should be a JSON object. To send a **batch SMS Engage** message here is an example of a data payload: ```json { "messages": [ { "templateVariables": { "firstName": "Igor", "order_nr": "1010101", "age": 24 }, "destination": "" }, { "templateVariables": { "firstName": "Petr", "order_nr": "1010104", "age": 20 }, "destination": "" }, { "templateVariables": { "firstName": "Vasia", "order_nr": "1010102", "age": 22 }, "destination": "" } ], "template": { "source": "YourBrand", "templateBody": "Hello {{firstName}}, your order is {{order_nr}}. Please check and confirm if your scheduled delivery time is ok: {{url}}" }, "clientBatchId": "MyBatch00001", "includeMessagesInResponse": true } ``` The JSON parameters are explained in the table below: | Parameter | Description | Example Value | | --- | --- | --- | | messages | Contains an array of at minimum templateVariables and destinations | {"templateVariables": {"firstName": "Petr","order_nr": "1010104","age": 20},"destination": ""}, | | template | Template of message to user. It is required to contain a **templateBody** that should have the variable **{{url}}**, this variable will be substituted in the SMS by the link set for you by 8x8.**Source** should also be included which is the Sender ID or 8x8 Virtual Number to send the SMS from. | "template": {"source": "YourBrand","templateBody": "Hello {{firstName}}, your order is {{order_nr}}. Please check and confirm if your scheduled delivery time is ok: {{url}}"}, | | clientBatchId | Custom string to associate with this batch of messages. | MyBatch001 | | includeMessagesInResponse | Whether to include the details of each individual message in the HTTP Response. | True | ### Sending the API Request Now that the API request is setup correctly you need, let’s try this by using cURL. If you are running **Mac OS**, **cURL** is already installed. Just run the **Terminal** app (Located under Applications->Utilities). For **Windows** based machines click [here](https://developer.zendesk.com/documentation/api-basics/getting-started/installing-and-using-curl/) on how to install curl. Alternatively you can use **Postman** if you are familiar with the tool as well to send this request. For **cURL** the command should look as follows, which can also be adapted into Postman. ```bash curl --location 'https://sms.8x8.com/api/v1/subaccounts//surveys//messages/batch' \ --header 'Content-Type: application/json' \ --header 'Authorization: ' \ --data '{ "messages": [ { "templateVariables": { "firstName": "Igor", "order_nr": "1010101", "age": 24 }, "destination": "" }, { "templateVariables": { "firstName": "Petr", "order_nr": "1010104", "age": 20 }, "destination": "" }, { "templateVariables": { "firstName": "Vasia", "order_nr": "1010102", "age": 22 }, "destination": "" } ], "template": { "source": "YourBrand", "templateBody": "Hello {{firstName}}, your order is {{order_nr}}. Please check and confirm if your scheduled delivery time is ok: {{url}}" }, "clientBatchId": "", "includeMessagesInResponse": true }' ``` Remember to replace the **,** **,** and values above. The **{{url}}** value does not need to be included in the **templateVariables** and will be automatically substituted by 8x8. This should result in a SMS being sent similar to the one below with the URL replacing the {{url}} parameter's place in the SMS Body. ![image](../images/5ece359-image.png) A successful request should return an 200 OK HTTP Response Code and the Response Body should appear as below. HTTP Response ```json { "batchId": "", "clientBatchId": "", "acceptedCount": 3, "rejectedCount": 0, "messages": [ { "umid": "", "clientMessageId": "/", "destination": "", "encoding": "GSM7", "status": { "code": "QUEUED", "description": "SMS is accepted and queued for processing" } }, { "umid": "", "clientMessageId": "/", "destination": "", "encoding": "GSM7", "status": { "code": "QUEUED", "description": "SMS is accepted and queued for processing" } }, { "umid": "", "clientMessageId": "/", "destination": "", "encoding": "GSM7", "status": { "code": "QUEUED", "description": "SMS is accepted and queued for processing" } } ] } ``` You should receive multiple messages in the **messages** array assuming you set **includeMessagesInResponse** as **true** in the request body. If it was set to **false** then the **messages** array will be **null**. Note that the **batchUmid**, **batchId**, **mobileNumber** and **umid's** values will be replaced with your request's unique values. ## Survey Appearance The webpages for the surveys are built by the 8x8 team for each customer based on their inputs. A few example pages are included below as they would appear in a mobile browser. They can include both text box and multiple choice type of questions. ![Survey Initial Page](../images/31dfa02-image.png)Survey Initial Page ![1-5 Rating Type Question](../images/89a35e5-image.png)1-5 Rating Type Question ![Long response type question](../images/b0e2b78-image.png)Long response type question The web pages are only **examples**, a survey webpage will be built by 8x8 as part of onboarding to SMS Engage that can be customised in terms of appearance and questions. please contact your account manager or send an email to [8x8 Support](mailto:cpaas-support@8x8.com). ## Reports Reports related to SMS engage, you can check the Reports section of the Connect Dashboard. For a given time period, reports will include the following information | Term | Description | | --- | --- | | Total Messages | The number of surveys sent by SMS engage. | | Clicks | The number of Surveys sent out that had the URL clicked. | | Responses | The number of participants who successfully completed the survey and sent back their responses. | | Click Rate | The percentage of total survey URLs that were clicked by recipients. Calculated as (Clicks / Total Messages) \* 100. | | Response Rate | The percentage of total survey URLs that resulted in a completed survey response. Calculated as (Responses / Total Messages) \* 100. | | Average Click Time | The average time between when an SMS message was sent and the time the survey URL was clicked. | | Average Response Time | The average time between when an SMS message was sent and the time a completed survey response was submitted. | ![image](../images/ada5457-image.png) For each survey response, clicking **View response** in the associated row will bring up the actual responses from the customer. ![image](../images/acf5739-image.png) The **Export** button on the same page will give you the option to email a CSV file of the responses in the survey. ![image](../images/58580f0-Screenshot_2024-06-25_at_5.57.53_AM.png) --- ## SMS Feedback API To ensure the efficient delivery of SMS verification codes to users' mobile devices, 8x8 provides our customers with the [Success Feedback API](/connect/reference/api-sms-feedback) (not applicable to SMPP). The Success Feedback API provides 8x8 insight into the success of your OTP verification code (by your OTP Generator) sent using our SMS API. For users who need an OTP Generator, may consider using [8x8 Verification API](/connect/reference/verification-api-get-started)( generate OTP and validate OTP message conversion). With 8x8 being aware of the conversion rate, it activates the Omni Shield for your subaccount. 8x8 will be able to detect any abnormal conversion rate proactively. The intelligent routing feature of the 8x8 platform is automatically triggered, seamlessly switching to pre-configured backup routes to carriers. Furthermore, 8x8's 24-hour technical operations team monitors network health around the clock, ensuring uninterrupted and smooth customer business operations 24/7. --- ## Prerequisites * Command line interface compatible with CURL * 8x8 account with SMS actovated with your subaccount. * apiKey (Bearer token) * 8x8 CPaaS subaccountid --- 1. **Tracking the Results on [SMS API](/connect/reference/send-sms-single)** ![image](../images/401511f-Screenshot_2023-12-05_at_9.58.33_AM.png) 2. **Sending the Outcome of OTP Conversion using [Feedback API](/connect/reference/api-sms-feedback)** 1. Success Feedback is a POST request 2. There are four parameters three of which are mandatory— `subAccountId`, `umid`, and `outcome` (success or failure) which are highlighted in red. The optional Parameter is the `timestamp` which provides time (UTC) and date reference which allows 8x8 Omni Shield to work promptly. > 📘 **Validity Period** > > The Success Feedback API call should be made within 15 minutes of the original message being sent. If the API call is not made during this timeframe then the data we receive may not accurately reflect your real conversion rate. > > ![image](../images/cadad9a-Success.png) 3. **Successful Post Request of Success Feedback API** Upon successfully pushing the Success Feedback API, it is normal for the HTTP 200 response to have no content returned. ![image](../images/ff90638-Screenshot_2023-12-05_at_10.11.39_AM.png) --- ## Tutorial: Customer Survey via WhatsApp ## Introduction In this tutorial, you'll learn how to create a WhatsApp chatbot that prompts users with survey questions and sends their responses to Google Sheets. This process can also be adapted for other survey tools like Qualtrics or SurveyMonkey. The default tutorial includes three questions, but you can customize it with your own questions and modify the workflow as needed. Use this tutorial as a foundation to develop your own tailored customer surveys. ## Prerequisites **Ensure you have:** * An **8x8** account * A **WhatsApp Business API** account with 8x8 * Access to **Google Sheets** and its API * You can also choose to substitute another sheet or survey tool. * **Pipedream Account** * You can also choose to substitute a similar API connector tool like Zapier, Make or even your own API server. ## Video Demo This is a accompanying video meant to show the WhatsApp Customer Survey as a demo. ## Step 1: Setup the Automation Builder Workflow You can find an example Automation Workflow workflow below, which you can use to import into the Automation Builder UI in [Connect](https://connect.8x8.com/automation). While we will not explain the different components of the tutorial here, if you need a refresher please see our section on Automation Builder [Steps](triggers-and-steps-library) and [Triggers](triggers). tutorial_wa_survey.json ```json { "definition": { "id": "65b79d9a-cc6c-4f04-834f-0afee555ba01", "name": "Tutorial G Sheets Survey", "version": 4, "steps": [ { "stepType": "ChatAppsMessage", "id": "send_ca", "do": [], "nextStepId": "waitforreply_9395", "inputs": { "subAccountId": "", "user": { "msisdn": "{{data.mobileNumber}}" }, "type": "text", "content": { "text": "Welcome to the 8x8 Customer Satisfication Survey. You will be asked 3 questions regarding our 8x8 Products.\n\nQuestion 1: What 8x8 Products do you Use?" } }, "outputs": { "mobileNumber": "variable 0", "customerName": "variable 1" }, "selectNextStep": {} }, { "stepType": "WaitForReply", "id": "waitforreply_9395", "do": [], "nextStepId": null, "inputs": { "from": "{{data.mobileNumber}}", "channel": "whatsapp", "timeout": "0.00:00:10" }, "outputs": { "waitforreply_9395_step_text": "{{step.reply.payload.content.text}}" }, "selectNextStep": { "chatappsmessage_1242": "{{ step.reply != null }}", "chatappsmessage_5691": "{{ step.reply == null }}" } }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_1242", "do": [], "nextStepId": "waitforreply_9504", "inputs": { "subAccountId": "", "type": "text", "content": { "text": "Question 2: How would you rate your overall experience from a scale of 10 to 1? \n\n10 being the best experience and 1 being the worst experience." }, "user": { "msisdn": "{{data.mobileNumber}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_5691", "do": [], "nextStepId": null, "inputs": { "subAccountId": "", "type": "text", "content": { "text": "No responses in allotted time, ending the survey. Your responses have not been recorded." }, "user": { "msisdn": "{{data.mobileNumber}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "WaitForReply", "id": "waitforreply_9504", "do": [], "nextStepId": null, "inputs": { "from": "{{data.mobileNumber}}", "channel": "whatsapp", "timeout": "0.00:00:10" }, "outputs": { "waitforreply_9504_step_text": "{{step.reply.payload.content.text}}" }, "selectNextStep": { "chatappsmessage_3769": "{{ step.reply != null }}", "chatappsmessage_7411": "{{ step.reply == null }}" } }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_3769", "do": [], "nextStepId": "waitforreply_9823", "inputs": { "subAccountId": "", "type": "text", "content": { "text": "Question 3: What are the major issues that you faced with 8x8 Products?" }, "user": { "msisdn": "{{data.mobileNumber}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_7411", "do": [], "nextStepId": null, "inputs": { "subAccountId": "", "type": "text", "content": { "text": "No responses in allotted time, ending the survey. Your responses have not been recorded." }, "user": { "msisdn": "{{data.mobileNumber}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "WaitForReply", "id": "waitforreply_9823", "do": [], "nextStepId": null, "inputs": { "from": "{{data.mobileNumber}}", "channel": "whatsapp", "timeout": "0.00:00:10" }, "outputs": { "waitforreply_9823_step_text": "{{step.reply.payload.content.text}}" }, "selectNextStep": { "chatappsmessage_0901": "{{ step.reply != null }}", "chatappsmessage_4456": "{{ step.reply == null }}" } }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_0901", "do": [], "nextStepId": "httprequest_4536", "inputs": { "subAccountId": "", "type": "text", "content": { "text": "Thank you for your responses to our survey! Your responses are being recorded." }, "user": { "msisdn": "{{data.mobileNumber}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "ChatAppsMessage", "id": "chatappsmessage_4456", "do": [], "nextStepId": null, "inputs": { "subAccountId": "", "type": "text", "content": { "text": "No responses in allotted time, ending the survey. Your responses have not been recorded." }, "user": { "msisdn": "{{data.mobileNumber}}" } }, "outputs": {}, "selectNextStep": {} }, { "stepType": "HttpRequest", "id": "httprequest_4536", "do": [], "nextStepId": null, "inputs": { "method": "POST", "url": "https://eo3yuu49vzo9m6.m.pipedream.net", "headers": {}, "parameters": {}, "body": {}, "timeoutSeconds": 30 }, "outputs": {}, "selectNextStep": {} } ] }, "subAccountId": "", "trigger": "http_request", "status": "enabled" } ``` Once the workflow is imported, it should appear within Automation Builder similar to the one below. ![image](../images/faf55c4-Jun-20-2024_16-59-04.gif) It is comprised of Messaging Apps, Branches, Wait for Replies and a single HTTP Request Step. If you choose to modify the flow you may also need to modify the request body to Pipedream at the end depending on the questions that you ask. ## Step 2: Setup Google Sheet Setup a Google Sheet with the following columns on your Google Account which we will use later within Pipedream to populate. ![image](../images/2f3913c-image.png) Here is an example table that you can be copy/pasted to your Google Sheet. | Mobile Number | Response 1:What 8x8 Products do you Use? | Response 2: How would you rate your overall experience from a scale of 10 to 1? | Response 3: What are the major issues that you faced with 8x8 Products? | | --- | --- | --- | --- | | | | | | ## Step 3: Setup Pipedream Setup a new workflow with an **HTTP Trigger** followed by a **Google Sheets: Add Single Row Step** ![image](../images/b4333bb-image.png) The **HTTP Trigger** should have these following configurations: ![image](../images/cc24cff-image.png) Within the **HTTP Trigger,** go to **Generate Test Event** and use the following JSON as the Test Event's input. This will allow us to correctly populate the values for the following Google Sheet step. ![image](../images/e2dff8a-Screenshot_2024-06-20_at_5.28.36_PM.png) ```json { "response1": "WhatsApp, SMS", "response2": "10", "response3": "No Issues!", "mobileNumber": "+6599999999" } ``` The **Google Sheets Step** should have the following configuration. ![image](../images/5ab2223-Screenshot_2024-06-20_at_5.30.40_PM.png) Within **Pipedream**, you should be able to see the HTTP responses sent by Automation Builder which may be useful in case any debugging is required. ![image](../images/f8264b4-Screenshot_2024-06-21_at_10.11.34_AM.png) ## Step 4: Send WhatsApp Message After setting up the above, you should be able to send a the WhatsApp Trigger message to your WhatsApp Account and complete the survey as follows. ![image](../images/6d1475c-image.png) This should result in a row being added to your Google Sheet with the response. ![image](../images/4338b18-image.png) ## Conclusion While we use **Google Sheets** in this tutorial, the same idea can be extended to a dedicated Customer Survey software like **Qualtrics, SurveyMonkey, Alchemer**, **etc**. Similarly while we used **Pipedream** for this tutorial, another tool that offers similar HTTP Trigger and Google Sheet Integration capabilities can also be used in it's place. **Expanded Explanation** * **Increased Response Rate:** Reduces friction, making it easier for users to participate. * **Higher Engagement Levels:** Uses interactive features to keep respondents interested. * **Enhanced Reach and Accessibility:** Broadens audience reach due to WhatsApp's popularity. * **Reduced Technical Issues:** Minimizes problems like slow loading and compatibility issues. We encourage to take this tutorial as a template and try it out with your own systems to craft a survey using automation builder. --- ## LINE Official Notification > 🚧 **LINE Official Notification requires your templates to be approved by LINE team before they can be sent. Reach out to [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) if you wish to submit new templates** > > > 👍 **LON uses [a different endpoint](https://chatapps.8x8.com/api/v1/subaccounts/%7BsubAccountId%7D/lon) compared to other Channels. Refer to [LON Send API](/connect/reference/send-lon-message) for the full API reference.** > > ### Sending an Event reminder notification This sample API request shows the full capability of LON message, with all the components included. Based on your preferred template you can modify or remove the label and contents of these messages. ```json { "user": { "msisdn": "+60000000" }, "smsFallback": { "text": "Event reminder", "source": "", "encoding": "auto" }, "content": { "title": "Event reminder", "company": "LINE", "icon": "calendarCheck", "emphasis": { "label": "Event name", "Content": "LINE Conference 2023" }, "list": [ { "label": "Date:", "content": "Tue 26/09/2023" }, { "label": "Time:", "content": "09:00 - 16:00" }, { "label": "Venue", "content": "LINE Office, 17th Fl." }, { "label": "Seat:", "content": "A-07" } ], "explanation": "We would like to remind you about your reservation for tomorrow event. The registration open at 09:00. See you soon.", "actions": [ { "title": "View agenda", "url": "https://gdconf.com/conference" }, { "title": "See direction", "url": "https://www.google.com/maps" } ] } } ``` The corresponding LON message received by the customer would look like: ![Sample LON message with all components included](../images/85b76de-LON_Event_Reminder.png) Sample LON message with all components included --- ## Line > 👍 **Please see [Messaging API](/connect/reference/send-message) for the full API reference.** ## Getting Started To start sending and receiving messages on LINE, you need a **LINE Official Account** with the **Messaging API** enabled. ### Prerequisites - An **8x8 Connect account** with Messaging Apps enabled. [Sign up here](https://connect.8x8.com) if you haven't already. - A **LINE Official Account** — create one at the [LINE Official Account Manager](https://manager.line.biz/). - Access to the **LINE Developers Console** at [developers.line.biz](https://developers.line.biz/). ### Channel Setup 1. In the [LINE Official Account Manager](https://manager.line.biz/), go to **Settings** > **Messaging API** and enable it. 2. In the [LINE Developers Console](https://developers.line.biz/), navigate to your Messaging API Channel and retrieve your: - **Channel ID** — a unique identifier for your LINE channel - **Channel Secret** — a secret key used to generate access tokens 3. Provide the **Channel ID** and **Channel Secret** to 8x8 — contact your account manager or [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) to configure the LINE channel on your sub-account. ### Webhook Configuration To receive inbound messages from LINE users: 1. In the [LINE Developers Console](https://developers.line.biz/), go to your Messaging API Channel 2. Under **Webhook settings**, set the **Webhook URL** to the URL provided by 8x8 3. Enable **Use webhook** > 📘 **Contact [cpaas-support@8x8.com](mailto:cpaas-support@8x8.com) to obtain the correct webhook URL for your account.** --- ## Sending a Text Message ```json { "user": { "lineUserId": "Ua12b345678c1de0fg1a1234567891011" }, "type": "Text", "content": { "text": "Hello from 8x8 Messaging API" } } ``` --- ## Sending an Image Message ```json { "user": { "lineUserId": "Ua12b345678c1de0fg1a1234567891011" }, "type": "Image", "content": { "url": "https://samplelib.com/png/sample-boat-400x300.png", "image": { "thumbnail": "https://samplelib.com/jpeg/sample-clouds-400x300.jpg" } } } ``` | Field | Description | | :---- | :---------- | | `content.url` | URL of the full-size image | | `content.image.thumbnail` | URL of the thumbnail preview image | --- ## Sending a Video Message ```json { "user": { "lineUserId": "Ua12b345678c1de0fg1a1234567891011" }, "type": "Video", "content": { "url": "https://samplelib.com/mp4/sample-5s.mp4", "video": { "thumbnail": "https://samplelib.com/png/sample-boat-400x300.png" } } } ``` | Field | Description | | :---- | :---------- | | `content.url` | URL of the video file | | `content.video.thumbnail` | URL of the thumbnail preview image | --- ## Sending an Audio Message ```json { "user": { "lineUserId": "Ua12b345678c1de0fg1a1234567891011" }, "type": "Audio", "content": { "url": "https://samplelib.com/mp3/sample-speech-5m.mp3", "audio": { "duration": 300 } } } ``` | Field | Description | | :---- | :---------- | | `content.url` | URL of the audio file | | `content.audio.duration` | Duration of the audio in seconds (required) | --- ## Sending a Location Message ```json { "user": { "lineUserId": "Ua12b345678c1de0fg1a1234567891011" }, "type": "Location", "content": { "location": { "latitude": 1.285651, "longitude": 103.847564, "name": "8x8 Office Singapore", "address": "One George Street, Singapore 049145" } } } ``` | Field | Description | | :---- | :---------- | | `content.location.latitude` | Latitude of the location | | `content.location.longitude` | Longitude of the location | | `content.location.name` | Name or title of the location | | `content.location.address` | Street address of the location | --- ## Receiving Inbound Messages When a LINE user sends a message to your LINE Official Account, 8x8 forwards it to your configured webhook URL. > 📘 You can configure your callback using the [Webhook Configuration API](/connect/reference/add-webhooks-1). ### Inbound Webhook Format | Field | Type | Description | | :---- | :--- | :---------- | | `eventType` | string | Always `inboundMessage` for inbound messages | | `channel` | string | Always `line` for Line messages | | `user.channelUserId` | string | The LINE user ID of the sender | | `umid` | uuid | Unique message ID for the inbound message | | `subAccountId` | string | ID of the sub-account receiving the message | | `timestamp` | string | UTC date and time in ISO 8601 format | | `type` | string | Message type: `Text`, `Image`, `Video`, `Audio`, `File`, or `Location` | | `content` | object | Message content (varies by type) | | `version` | integer | Webhook format version | | `recipient.recipientId` | string | ID of the LINE channel that received the message | ### Inbound Text Message ```json { "eventType": "inboundMessage", "channel": "line", "user": { "channelUserId": "Ua12b345678c1de0fg1a1234567891011" }, "umid": "9e09ac86-bd74-5465-851d-1eb5a5fdbb9a", "subAccountId": "yourSubAccountId", "timestamp": "2026-06-18T05:15:30.00Z", "type": "Text", "content": { "text": "Hello from LINE" }, "version": 1, "recipient": { "recipientId": "7ee31a3f-9ed7-49f6-800f-a697e687553f" } } ``` ### Inbound Image Message ```json { "eventType": "inboundMessage", "channel": "line", "user": { "channelUserId": "Ua12b345678c1de0fg1a1234567891011" }, "umid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "subAccountId": "yourSubAccountId", "timestamp": "2026-06-18T05:16:10.00Z", "type": "Image", "content": { "url": "https://s3.ap-southeast-1.amazonaws.com/wavecell.chatapps/20260618/18289/a1b2c3d4-e5f6-7890-abcd-ef1234567890.jpg?X-Amz-Expires=86400&..." }, "version": 1, "recipient": { "recipientId": "7ee31a3f-9ed7-49f6-800f-a697e687553f" } } ``` > 📘 Media URLs (image, video, audio, file) are pre-signed S3 URLs that expire after 24 hours. ### Inbound Video Message ```json { "eventType": "inboundMessage", "channel": "line", "user": { "channelUserId": "Ua12b345678c1de0fg1a1234567891011" }, "umid": "e7dc3fb1-d2e5-404c-927c-b46d0056fa6e", "subAccountId": "yourSubAccountId", "timestamp": "2026-06-18T05:16:40.63Z", "type": "Video", "content": { "url": "https://s3.ap-southeast-1.amazonaws.com/wavecell.chatapps/20260618/18289/e7dc3fb1-d2e5-404c-927c-b46d0056fa6e.mp4?X-Amz-Expires=86400&..." }, "version": 1, "recipient": { "recipientId": "7ee31a3f-9ed7-49f6-800f-a697e687553f" } } ``` ### Inbound Audio Message ```json { "eventType": "inboundMessage", "channel": "line", "user": { "channelUserId": "Ua12b345678c1de0fg1a1234567891011" }, "umid": "b2c3d4e5-f6a7-8901-bcde-f12345678901", "subAccountId": "yourSubAccountId", "timestamp": "2026-06-18T05:17:10.00Z", "type": "Audio", "content": { "url": "https://s3.ap-southeast-1.amazonaws.com/wavecell.chatapps/20260618/18289/b2c3d4e5-f6a7-8901-bcde-f12345678901.m4a?X-Amz-Expires=86400&..." }, "version": 1, "recipient": { "recipientId": "7ee31a3f-9ed7-49f6-800f-a697e687553f" } } ``` ### Inbound File Message ```json { "eventType": "inboundMessage", "channel": "line", "user": { "channelUserId": "Ua12b345678c1de0fg1a1234567891011" }, "umid": "c3d4e5f6-a7b8-9012-cdef-123456789012", "subAccountId": "yourSubAccountId", "timestamp": "2026-06-18T05:17:30.00Z", "type": "File", "content": { "url": "https://s3.ap-southeast-1.amazonaws.com/wavecell.chatapps/20260618/18289/c3d4e5f6-a7b8-9012-cdef-123456789012.jpg?X-Amz-Expires=86400&..." }, "version": 1, "recipient": { "recipientId": "7ee31a3f-9ed7-49f6-800f-a697e687553f" } } ``` ### Inbound Location Message ```json { "eventType": "inboundMessage", "channel": "line", "user": { "channelUserId": "Ua12b345678c1de0fg1a1234567891011" }, "umid": "46d27247-ecda-4bdd-8b17-b46d00575299", "subAccountId": "yourSubAccountId", "timestamp": "2026-06-18T05:17:55.35Z", "type": "Location", "content": { "location": { "longitude": 103.846375, "latitude": 1.289563, "name": "Clarke Quay Riverside", "address": "Clarke Quay, 179019" } }, "version": 1, "recipient": { "recipientId": "7ee31a3f-9ed7-49f6-800f-a697e687553f" } } ``` --- ## Message types and samples(Docs) > 👍 **Please see [Messaging API](/connect/reference/send-message) for the full API reference.** > > ## Text Message **Content:** Text only **Character Limit:** Up to 3072 characters **Use Cases:** OTP codes, simple alerts ### Payload sample ```json { "user": { "msisdn": "+10000000000" }, "type": "Text", "content": { "text": ":wave: Hi Sarah! Just a reminder—your appointment at Wellness Dental is scheduled for tomorrow at 10:30 AM" } } ``` The corresponding message the user will receive: ![image](../images/922cb3b165f5e7b3b750e82d80d1dbcd0d41f95d3cb74c097d1e050789d0d4cf-Text.png) --- ## Sending a Rich Media Message * Media Types Supported: Images, videos, documents * File formats: .ogx, .pdf, .aac, .mp3, .mpeg, .mp3, .mp4, .mp4, .3gp, .jpeg, .jpg, .gif, .png, .h263, .m4v, .mp4, .mp4, .mpeg, .webm * Text Caption: Up to 2,000 UTF-8 characters * File Size Limits: 100MB * File URL limit: 2,048 characters ### Image & text ```json { "user": { "msisdn": "+10000000000" }, "type": "Image", "content": { "url": "https://www.example.com/image.jpg", "text": "Hi Suzie! Meet Bruno :feet:—one of the many pups looking for a loving home. Every small donation helps us feed, shelter, and care for dogs like him." } } ``` The corresponding message the user will receive: ![image](../images/c5f9de293d906f4fe09f184f2519b58ff8e02f347fa4acbbca6b780366766422-Image_6.png) --- ### Video & text ```json { "user": { "msisdn": "+10000000000" }, "type": "Video", "content": { "url": "https://www.example.com/video.mp4", "text": "Hi Suzie! Meet Bruno :feet:—one of the many pups looking for a loving home. Every small donation helps us feed, shelter, and care for dogs like him." } } ``` The corresponding message the user will receive: ![image](../images/602dd6dd429b4863b5c199b6d2e49d8e05bc4765c2a8173fa9dea5f1cb1d4922-Video.png) --- ### Audio & text ```json { "user": { "msisdn": "+10000000000" }, "type": "Audio", "content": { "url": "https://www.example.com/video.mp4", "text": "Hi There, this is a Sample RCS Audio Message" } } ``` The corresponding message the user will receive: --- ### File & text ```json { "user": { "msisdn": "+10000000000" }, "type": "Text", "content": { "url": "https://example.com/links/Invoice-october-2025.pdf", "text": "Hey John! Here’s your monthly invoice for October. Contact our team if you have any questions. Thank you" } } ``` The corresponding message the user will receive: ![image](../images/635d3e126566938536a5d8c5422384bafbf5b042bcf545aa09df5f48325294ec-File.png) --- ## Suggested Actions Suggestions in RCS Business Messaging provide interactive buttons, chips, or quick replies that guide users seamlessly through rich conversational experiences. By using suggestions, brands can streamline user journeys, enhance engagement, improve conversions, and gather immediate user feedback. ### Available Suggestion Types | Suggestion type | One-line description | Typical brand use cases | Core benefit | | --- | --- | --- | --- | | **Suggested Reply** | Sends a predefined text back to your agent or bot. | *Yes/No*, choose size/colour, CSAT “👍/👎”, OTP confirmation. | Keeps flow structured and speeds funnel completion. | | **Dial a Number** | Opens the dialer with a preset phone number. | Escalate to live agent, click-to-call for abandoned carts, fraud alerts. | Instant voice escalation builds trust and saves high-value sales. | | **View a Location** | Launches maps focused on a given pin or search term. | Store locator, nearest ATM/locker, travel itinerary. | Drives measurable footfall from messaging. | | **Open URL / Webview** | Opens browser or in-app webview (full/half/tall). | Secure checkout, product page, claim form, loyalty sign-in. | Seamless upsell without forcing an app download. | | **Create Calendar Event** | Pre-fills a calendar entry in the user’s default calendar. | Doctor appointments, flight reminders, webinar invites. | Cuts no-shows by embedding reminders directly in the calendar. | ![Appointment confirm](../images/2f3128a1c6a2038bda2b9f5d6c9b2ddcc6149da519ba6edfdbfced18cfd6ed7b-Appointment_confirm.png) ### Best practices * Limit to 4‑5 suggestions per message to avoid cognitive overload. * Use clear, action‑oriented labels (e.g. “Track Order” instead of “Order”). * Always set postback data so downstream systems can act on replies. * Include capability fallback (SMS or URL) when the user’s client does not support a given action. * Instrument analytics to track tap‑through and optimise suggestion wording. ### Implementation Example ```json "user": { "msisdn": "+10000000000" }, "type": "Text", "content": { "text": ":wave: Hi Sarah! Just a reminder—your appointment at Wellness Dental is scheduled for tomorrow at 10:30 AM", "suggestions": [ { "reply": { "text": "Confirm", "postbackData": "user_confirmed" } }, { "reply": { "text": "Reschedule", "postbackData": "user_rescheduled" } }, { "action": { "text": "Add to Calendar", "postbackData": "add_event_to_calendar", "createCalendarEventAction": { "title": "Doctor Appointment", "description": "Annual health checkup at City Medical Center", "startTime": "2025-07-25T10:30:00Z", "endTime": "2025-07-25T11:00:00Z" } } }, { "action": { "text": "View Location", "postbackData": "view_clinic_location", "viewLocationAction": { "latLong": { "latitude": 37.7749, "longitude": -122.4194 }, "label": "Star Clinic", "query": "clinics near me" } } }, { "action": { "text": "Check our website", "postbackData": "open_product_page", "openUrlAction": { "url": "https://cpaas.8x8.com/en/", "application": "WEBVIEW", "webviewViewMode": "FULL", "description": "View product details" }, "fallbackUrl": "https://example.com/fallback" } } ] } }' ``` ## Overview of file types and limits ### Supported **File** formats are | Category | Extensions / MIME types | Notes | | --- | --- | --- | | **Images** | `.jpeg` / `.jpg` (`image/jpeg`), `.png` (`image/png`), `.gif` (`image/gif`) | Supported in rich cards & media messages | | **Video** | `.h263` (`video/h263`), `.m4v` (`video/m4v`), `.mp4` (`video/mp4`, `video/mpeg4`), `.mpeg` (`video/mpeg`), `.webm` (`video/webm`) | Supported in rich cards & media messages | | **Audio** | `.aac` (`audio/aac`), `.mp3` (`audio/mp3`, `audio/mpeg`, `audio/mpg`), `.mp4` (`audio/mp4`, `audio/mp4-latm`), `.3gp` (`audio/3gpp`), `.ogx` / `.ogg` (`application/ogg`, `audio/ogg`) | Media messages only | | **Documents** | `.pdf` (`application/pdf`) | Media messages (not rich cards) | | **File size cap** | Up to **100 MB** per attachment | | ### Limits | Message element / field | Limit | | --- | --- | | **Plain text message** | 3 072 characters | | **Rich-card title** | 200 characters | | **Rich-card description** | 2 000 characters | | **Suggested-reply text** | 25 characters | | **Suggested-action text** | 25 characters | | **Suggestion chips per message** | Up to 11 chips (4 in-card + 7 extra) | | **Carousel cards per message** | Up to 10 cards | | **Text caption with media** | 2 000 characters | | **Postback data** (per suggestion) | 2 048 characters | | **Rich-card payload size** | 250 KB | --- ## Usage samples > ❗️ **Customer Service Window** > > WhatsApp only allows freeform text messages to be sent once a [customer service window](https://developers.facebook.com/docs/whatsapp/pricing/#customer-service-windows) has started. A customer service window starts when a user initiates a conversation or when a user replies to a pre-approved template sent by the business. > > This customer service window lasts 24 hours, and lasts 72 hours if the customer service window is initiated via a [click-to-whatsapp ad](https://business.whatsapp.com/products/ads-that-click-to-whatsapp). > > Outside of the customer service window, only pre-approved WhatsApp templates can be sent to users. > > > 👍 **Please see [Messaging API](/connect/reference/send-message) for the full API reference.** > > ## Freeform messages ### Text message If you want to **send a text message**, your request will look like this: ```json { "user": { "msisdn": "+65000000", "channelUserId": "US.13491208655302741918" }, "type": "text", "content": { "text": "Thank you for your recent purchase from TechStore! If you have any questions or need support, reply 'HELP' to connect with our support team." } } ``` The user will receive this corresponding message: ![image](../images/b08b935-image.png) --- ### Text message with an image If you want to send an image with an optional `text`, your request will look like this: ```json { "user": { "msisdn": "+6500000000", "channelUserId": "US.13491208655302741918" }, "content": { "url": "https://www.example.com/image.jpg", "text": "Welcome to the world of 8x8 ChatApps APIs!\nCommunications for the customer obsessed." }, "type": "Image" } ``` The user will receive this corresponding message with the corresponding image from the URL that you specify. ![image](../images/c1ce5fe-image.png) --- ## Template Messages ### Template message with text only Depending on the use case and content, your template submitted can be categorised as a Marketing or Utility template. This template has a single parameter where you can specify the actual OTP code in your API call. ```json { "user": { "msisdn": "+65000000", "channelUserId": "US.13491208655302741918" }, "type": "template", "content": { "template": { "language": "en_GB", "name": "