# Quick Start

Welcome to the official Cuoral Developer Documentation!\
\
Here you’ll find everything you need to start integrating with the Cuoral platform to deliver proactive, AI-powered customer experiences at scale.

Cuoral helps businesses reduce silent churn, resolve issues before users complain, and drive continuous product improvement - all through intelligent engagement workflows across chat, email, and SMS.

***

### What You Can Do With the Cuoral API

* Create and manage customer companies and contacts
* Trigger automated engagement workflows
* Fetch customer feedback and issue logs
* Analyze product experience trends in real-time
* Integrate Cuoral into your internal tools or customer systems

Whether you're building a custom support dashboard, syncing user data, or triggering proactive messaging - our APIs are designed to be simple, fast, and powerful.

***

### 🔑 Get Your API Key

To use the Cuoral API, you’ll need an API Key scoped to your organization\
\
[Sign up here to get your API key](https://cuoral.com/)\
\
Once signed in, head to the **Settings → API Access** section to generate and manage your keys.

Your API key must be included in the *<mark style="color:$warning;">x-api-key</mark>* header for all requests:

```http
x-api-key: YOUR_API_KEY
```

***

### What’s Next?

* Start with the **Company API** to create and manage customer organizations
* Explore **Contact APIs** for individual users
* Engagement APIs for customer outreach
* Use **Workflow APIs** to automate messaging and feedback collection

Need help? Reach out to our team anytime at <team@cuoral.com>

Happy Building! 💡

## Want to deep dive?

Dive a little deeper and start exploring our API reference to get an idea of everything that's possible with the API:

{% content-ref url="/pages/lIZup5Xx4KyTd8QFCx1U" %}
[Customers](/api-reference/customers)
{% endcontent-ref %}


# Customer Intelligence

Cuoral Customer Intelligence is the brain behind how businesses understand, measure, and act on customer experiences in real time.

It automatically tracks how users interact with your product — from clicks and navigation to errors and drop-offs - then turns that data into actionable insights your team can use to improve reliability, reduce churn, and drive engagement.

At its core, Customer Intelligence helps you answer three key questions:

1. **Where are customers struggling or getting stuck?**
2. **Which interactions lead to churn or drop-offs?**
3. **How can we proactively resolve friction before it becomes a ticket?**

Whether it’s identifying users who haven’t logged in for 7 days, spotting checkout errors, or detecting high drop-off rates on a key page, Cuoral continuously surfaces insights that matter most — so teams can act, not react.

***

### 🚨 Alerting System

Cuoral’s alerting engine keeps your team one step ahead.

It automatically detects **friction patterns, anomalies, and risk signals** — then triggers alerts through your preferred channel (email, dashboard notification, or team workspace integration).

You can define **custom rules and thresholds**, such as:

* Alert when user engagement drops by 30% in a week
* Notify support when an unusual number of users face login or payment issues
* Flag inactive premium users after 5 days of no activity

These alerts are powered by Cuoral’s AI models, which analyze behavioral and system-level signals to determine when something requires attention.

This means your team doesn’t have to constantly monitor dashboards — Cuoral does it for you, so you can focus on building better customer experiences.


# What We Monitor

Cuoral collects and analyzes multiple streams of customer-interaction data, all linked to each session, customer, and organization.

Here’s what’s being monitored under the hood 👇

***

#### 1. Console Errors

**Purpose:** Detect client-side issues affecting customer experience — e.g., failing scripts, broken UI components, or unhandled exceptions.

* Error messages and stack traces
* URL where the error occurred
* Severity level (error, warn, info, debug)
* Associated session, customer, and organization
* Additional context (browser, environment metadata)

> “12% of active sessions encountered console errors — likely affecting checkout conversion.”

***

#### 2. API Response Logs

**Purpose:** Identify backend errors, failed integrations, and slow endpoints impacting customers.

* Request URL and HTTP method
* Response status codes and payloads
* Latency and metadata
* Associated customer and session context

> “Average response time increased by 45% for `/payment/initiate` endpoint — customers may be experiencing transaction delays.”

***

#### 3. Page Views & Navigation Events

**Purpose:** Measure engagement and pinpoint drop-off points or friction patterns.

* Page URLs and referrers
* User agents (browser/device info)
* Metadata such as time on page or scroll depth
* Customer, session, and organization linkage

> “45% of users drop off after visiting the pricing page — consider optimizing call-to-action.”

***

#### 4. Recorded Media (Session Replay & Artifacts)

**Purpose:** Provide visual context for reported issues or customer interactions, reducing time-to-resolution for support teams.

* Secure URLs for session replays or recordings
* Notes or annotations from support teams
* Context on customer, organization, and session

***

#### 5. Session Intelligence (Aggregated Metrics)

**Purpose:** Give organizations a summarized view of each customer’s journey quality and alert when attention is needed.

* Total error count
* API error rate
* Average response time
* Page view count / engagement activity
* Engagement score (based on interaction richness)
* Churn risk score (based on inactivity and issue frequency)

> “Session 34215 shows high churn risk — multiple API errors and zero engagement in the last 5 minutes.”

***

### 🧩 Integrated Relationships

Each record Cuoral captures is linked across your ecosystem, enabling full visibility and context.

| Entity                     | Linked To                            | Example Use                         |
| -------------------------- | ------------------------------------ | ----------------------------------- |
| **Organization**           | All events                           | Organization-wide health reports    |
| **Customer**               | Sessions, logs, views                | Individual experience analysis      |
| **Session (Conversation)** | API logs, console errors, page views | Complete interaction timeline       |
| **RecordMedia**            | Errors, APIs, Pages                  | Visual debugging & support evidence |
| **Tickets**                | Errors & API logs                    | Context-rich support automation     |

### 🔐 Privacy & Data Control

* All monitored data is linked via anonymized session and customer IDs.
* Sensitive payloads are redacted by default.
* Cuoral supports GDPR compliance and organization-level data retention policies.

***

### 🧭 Summary

| Category         | Description                                                                           |
| ---------------- | ------------------------------------------------------------------------------------- |
| **Focus**        | End-to-end customer experience monitoring                                             |
| **Core Modules** | Console Errors, API Logs, Page Views, Media Records, Session Intelligence             |
| **Output**       | Alerts, analytics dashboards, proactive insights                                      |
| **Goal**         | Empower Customer Success, Support, and Product teams to act before customers complain |


# How it Helps Your Teams

| Team                 | What They Get                                           | Example Outcome                                                 |
| -------------------- | ------------------------------------------------------- | --------------------------------------------------------------- |
| **Customer Success** | Real-time engagement insights & churn risk alerts       | Reach out proactively to customers showing signs of frustration |
| **Customer Support** | Contextual ticket enrichment with session logs & errors | Faster resolution with less back-and-forth                      |
| **Product**          | Visibility into error trends & API performance          | Identify product bugs or UX bottlenecks before they escalate    |
| **Engineering**      | Detailed error, trace, and latency logs                 | Quicker root-cause analysis for incidents                       |

***

### Example Insights Delivered

* ⚠️ 18% of all sessions this week had API error rates above 10%.
* 🧩 Customer ID 1043 experienced repeated 400 errors on `POST /checkout`.
* 💡 Average engagement score increased by 24% after UI redesign.
* 🔔 Alert: Churn risk increasing for 5 high-value customers.

***

### Proactive Alerts and Intelligence

Cuoral continuously analyzes session intelligence to trigger automated alerts through:

* Dashboard notifications
* Email summaries
* Webhook events or integrations (e.g., Slack, Zendesk, HubSpot)

**Alert types include:**

* High error frequency
* Low engagement / churn signals
* Repeated API failures
* High latency spikes
* Sudden drop in page interactions

Alerts can be configured **hourly, daily, or weekly**, depending on organizational preference.

***


# Configuration

A complete guide to configuring and customizing your Cuoral platform for optimal customer engagement and support operations.

***

### **Table of Contents**

1. Appearance Configuration
2. Bot Training
3. Proactive Alerts
4. Activity Segments
5. Widget Setup
6. Ticketing Configuration
7. Integrations

***

## **1. Appearance Configuration**

**Tab:** Appearance\
**Purpose:** Customize how your support widget looks and behaves.

***

### **1.1 Widget Appearance**

Customize the visual design and branding of your widget.

#### **Configuration Options**

* **Widget Color Theme** — Match your brand identity
* **Widget Position** — Left or right side
* **Welcome Image** — Collage, Modern, Classic
* **Support Name** — e.g., “Support Team”
* **Button Label** — e.g., “Chat Now”
* **Online Status Message** — Custom availability text
* **Widget Sound** — Toggle notifications
* **White Label (Premium)** — Remove Cuoral branding

#### **Steps**

1. Go to **Configuration → Appearance**
2. Open **Widget Appearance**
3. Update settings
4. Click **Save Changes**

#### **Best Practices**

* Use brand-consistent colors
* Keep labels short and action-oriented
* Test responsiveness across devices

***

### **1.2 Support Hours**

Define when your team is available.

#### **Configuration Options**

* Timezone
* Daily schedules
* Multiple time slots
* Days off

#### **Steps**

1. Go to **Appearance → Support Hours**
2. Select timezone
3. Add time slots per day
4. Save schedule

#### **Behavior**

* Shows offline message outside hours
* Sets response expectations
* Drives automation behavior

#### **Best Practices**

* Be realistic about coverage
* Add buffer time
* Update for holidays

**Plan:** Basic+

***

### **1.3 Message Reminders**

Automated reminders for unanswered messages.

#### **Configuration Options**

* Reminder intervals
* Notification channels (email, in-app, Slack)

#### **Steps**

1. Go to **Appearance → Message Reminders**
2. Add intervals (e.g., 10, 30, 60 mins)
3. Save

#### **Behavior**

* Sends reminders if no response
* Stops after reply

#### **Best Practices**

* Start with 5–10 min interval
* Add 1–2 escalation reminders
* Avoid over-notifying

**Plan:** Standard+

***

### **1.4 Intelligence Features**

AI-powered automation tools.

#### **Options**

* Customer Intelligence
* AI Agent

#### **Steps**

1. Go to **Widget Appearance**
2. Enable toggles
3. Save

#### **Behavior**

* Insights from user behavior
* Automated responses

#### **Best Practices**

* Train AI before enabling
* Monitor early responses
* Use insights for retention

**Plan:**

* Customer Intelligence → Standard+
* AI Agent → Premium

***

## **2. Bot Training**

**Tab:** Bot Training\
**Purpose:** Train your AI assistant.

***

### **Training Methods**

#### **2.1 File Upload**

Supported formats:

* TXT, PDF, Markdown, Word

**Steps**

1. Add Training Data → File Upload
2. Upload file
3. Wait for processing

**Best Practices**

* Use structured docs
* Break into smaller files
* Keep updated

***

#### **2.2 Website URL Training**

**Steps**

1. Add Training Data → Website URL
2. Enter base URL
3. Set refresh frequency
4. Save

**Behavior**

* Crawls site and subpages
* Extracts training data

**Best Practices**

* Use documentation URLs
* Ensure public access
* Prefer focused sections

***

#### **2.3 Q\&A Pairs**

**Steps**

1. Add Training Data → Q\&A Pair
2. Enter question + answer
3. Save

**Best Practices**

* Add common questions
* Use conversational tone
* Include variations

***

#### **2.4 Managing Training Data**

* **View**: See all sources
* **Edit**: Update entries
* **Delete**: Remove entries

**Statuses**

* Active
* Processing
* Failed

***

## **3. Proactive Alerts**

**Tab:** Proactive Alerts\
**Purpose:** Detect issues and notify your team.

***

### **3.1 Alert Triggers**

Available triggers:

* Technical Assistance
* API Failure
* Re-engagement
* Billing Abandonment
* Slow/Failing Actions
* Upsell
* Onboarding
* Feature Discovery

#### **Steps**

1. Enable triggers
2. Save

#### **Best Practices**

* Start with 2–3 triggers
* Monitor alert volume
* Create response playbooks

***

### **3.2 Notification Channels**

* Email
* In-app
* Push
* Slack

#### **Best Practices**

* Use multiple channels for critical alerts
* Avoid over-notification

***

### **3.3 Confidence Threshold**

* Default: 70%
* Higher → fewer false positives
* Lower → more alerts

***

### **3.4 Team Notification Settings**

* Admin
* Agents
* Custom roles

***

### **3.5 Quiet Hours**

* Suppresses alerts during off-hours
* Queues notifications

***

### **3.6 Testing Alerts**

* Send test alerts
* Verify delivery and clarity

**Plan:** Basic+

***

## **4. Activity Segments**

**Tab:** Activity Segments\
**Purpose:** Categorize users by engagement.

***

### **4.1 Segment Types**

* Active
* Slightly Active
* Dormant
* Inactive

***

### **4.2 Custom Thresholds**

Adjust inactivity days.

#### **Examples**

* SaaS
* E-commerce
* B2B

***

### **4.3 Reset to Defaults**

Defaults:

* Active: 7 days
* Slightly Active: 30 days
* Dormant: 60 days

***

### **4.4 Usage**

* Filter users
* Track churn risk
* Analyze engagement

***

### **4.5 Automation**

* Re-engagement campaigns
* Support prioritization
* Product insights

***

### **4.6 Best Practices**

* Review weekly
* Build playbooks
* Track movement

***

## **5. Widget Setup**

**Tab:** Widget Setup\
**Purpose:** Install Cuoral widget.

***

### **5.1 Web (HTML/JS)**

**Steps**

1. Copy script
2. Add before `</body>`
3. Deploy

#### **Optional Config**

```
s.dataset.marginBottom = "20";
s.dataset.marginRight = "20";
s.dataset.email = "user@example.com";
```

***

### **5.2 Flutter**

```
dependencies:
  cuoral_flutter: ^0.0.3
```

```
CuoralLauncher(
  publicKey: 'your-public-key',
)
```

***

### **5.3 React Native**

```
npm install cuoral-react-native
```

```
<CuoralWidget publicKey="your-public-key" />
```

***

### **5.4 Ionic**

```
npm install cuoral-ionic
```

```
this.cuoral = new Cuoral({
  publicKey: 'your-public-key'
});
```

***

### **General Notes**

* Public key is safe for frontend
* Test before production

***

## **6. Ticketing Configuration**

**Tab:** Ticketing

***

### **6.1 Customer Ticket Creation**

* Enable or disable user-created tickets

**Plan:** Advanced+

***

### **6.2 Auto-Assignment Rules**

#### **Steps**

1. Enable auto-assignment
2. Add rules
3. Assign by tag

***

### **6.3 Ticket Tags**

Default:

* Billing
* Technical
* Sales
* Bug Report

Custom tags supported

***

### **6.4 Example Rules**

* Billing → Finance
* Technical → Engineering

***

### **6.5 Team Roles**

* Define assignment groups
* Assign users to roles

***

### **6.6 Best Practices**

* Start simple
* Maintain tag consistency
* Monitor workload

***

### **Best Practices**

* Review permissions
* Manage notifications
* Test integrations
* Audit regularly


# Widget Setup

Connect **Cuoral** to your website or mobile app. Choose your platform below:

* Web (HTML/JS)
* Flutter
* React Native
* Ionic (Capacitor)

***

## Web Integration (HTML / JavaScript)

This single script tag connects the Cuoral widget to your website.

We recommend placing this snippet on every page where the widget should appear, **right before the closing `</body>` tag**.

### 🔑 Your Public Key

```
<your public key>
```

***

### Integration Code

Place this code just before the closing `</body>` tag:

```javascript
<!-- Place it just before the closing </body> tag. -->
<script>
  (() => {
    // Create the script element
    const s = document.createElement('script');
    
    // Set the source URL and defer loading
    s.src = 'https://js.cuoral.com/inline.js'; 
    s.defer = true;
    
    s.dataset.cuoralKey = "<your public key>";

    // Pass User Details (for personalized support)
    try {
      const tm = JSON.parse(localStorage.getItem("user") || "{}");
      
      if (tm.email) {
        s.dataset.email = tm.email;
        s.dataset.first_name = tm.first_name || "";
        s.dataset.last_name = tm.last_name || "";
      }
    } catch (e) { 
      // Handle parsing error silently
    }
    
    
    // Add custom positioning (optional - adjust values as needed):
    // s.dataset.marginBottom = "20";  // in px (or use s.dataset.margin_bottom)
    // s.dataset.marginRight = "20";   // in px (or use s.dataset.margin_right)
    // s.dataset.marginLeft = "";      // in px (or use s.dataset.margin_left)
    // s.dataset.marginTop = "";       // in px (or use s.dataset.margin_top)
    
    document.head.appendChild(s);
  })();
</script>
```

#### Optional: Passing User Details

To personalize support conversations, pass the currently logged-in user’s information. Replace the `localStorage` logic with however your app retrieves authenticated user data.

***

#### Log user out of a session

```javascript
// to log user out of the web application
window.CuoralWidget.logout();
```

Need help with integration? Contact our support team.

***

## Flutter App Integration

Use the official `cuoral_flutter` package to embed the chat widget directly into your Flutter application.

### Step 1: Add Dependency

Open your `pubspec.yaml` file and add:

```yaml
dependencies:
  flutter:
    sdk: flutter
  cuoral_flutter: ^0.0.3 # Check pub.dev for the latest version
```

***

### Step 2: Initialize CuoralLauncher

Import the library and place the `CuoralLauncher` widget where you want the floating button to appear (typically inside a `Stack` or `Scaffold`).

```dart
import 'package:cuoral_flutter/cuoral_flutter.dart';
import 'package:flutter/material.dart';

CuoralLauncher(
  publicKey: '<your public key>', // REQUIRED
  
  // Optional Configuration
  backgroundColor: Colors.indigoAccent,
  icon: Icon(Icons.chat_bubble, color: Colors.white),
  isVisible: true,
  position: Alignment.bottomRight,
);
```

For more details, visit pub.dev and search for `cuoral_flutter`.

***

Need help with integration? Contact our support team.

***

## React Native & Expo Integration

We support:

* **Standard React Native (CLI)** → `cuoral-react-native`
* **Expo Projects** → `cuoral-react-native-expo`

***

### Step 1: Install Package

```javascript
npm install <package-name>
```

Replace `<package-name>` with:

* `cuoral-react-native`
* or `cuoral-react-native-expo`

***

### Step 2: Usage

```javascript
import CuoralWidget from 'cuoral-react-native';
import { View } from 'react-native';

const App = () => {
  const currentUser = { 
    email: "user@example.com", 
    firstName: "Alex" 
  }; 

  return (
    <View style={{ flex: 1 }}>
      <CuoralWidget
        publicKey="<your public key>"
        user={{ 
          email: currentUser.email,
          firstName: currentUser.firstName,
        }} 
      />
    </View>
  );
};

export default App;
```

***

Need help with integration? Contact our support team.

***

## Ionic & Capacitor Integration

Easily add Cuoral support to your Ionic application using our npm package.

***

### Step 1: Install the Package

```
npm install cuoral-ionic
```

If using Capacitor:

```
npx cap sync
```

***

### Step 2: Initialize Cuoral

In your `app.component.ts`:

```javascript
import { Cuoral } from 'cuoral-ionic';

private cuoral: Cuoral;

constructor() {
  this.cuoral = new Cuoral({
    publicKey: '<your public key>',
    showFloatingButton: true,
    useModal: true
  });
}

async ngOnInit() {
  await this.cuoral.initialize();
}

ngOnDestroy() {
  this.cuoral.destroy();
}
```

For more details, visit npm and search for `cuoral-ionic`.


# Whatsapp

This guide walks you through how to activate and configure the WhatsApp integration for your account.

### Prerequisites

Before starting the setup process, ensure that:

* You have admin access to your dashboard
* The WhatsApp number you intend to use is active
* Your wallet is funded (required for message delivery)

***

### Step-by-Step Setup

#### 1. Navigate to Integrations

Go to:

**Settings → Configuration Menu → Integrations Tab → WhatsApp**

Click on **WhatsApp** to begin the setup process.

> 📸 *WhatsApp integration entry point*\
> ![](/files/dYbbj7vB7MFVZf6nY8nd)<br>

***

#### 2. Enter Phone Number

You will be prompted to provide the WhatsApp phone number you want to connect.

After entering the number, click the **Setup** button to proceed.

> 📸 *Phone number input modal*\
> ![](/files/zjPBu2JzjTIrN1hWX8OL)

***

#### 3. Grant WhatsApp Access

You will be redirected to Facebook to complete the authorization process. This may require:

* Logging into your Facebook account
* Granting permission for the selected WhatsApp number
* Providing an OTP linked to the phone number\
  ![](/files/ohPLyrZXhMTD7OKvXpEv)

Complete the process as instructed.

***

#### 4. Approval Waiting Period

Once authorization is completed, access to the WhatsApp number will be granted. However, full approval may take **up to 24 hours (or less)**.

After approval:

* All incoming WhatsApp messages will appear on the **Conversation Page**
* Your team can start replying directly from the dashboard

> <img src="/files/nkiRVnXx7bRZvXAPd1Zl" alt="" data-size="original">

***

### Important Note: Wallet Funding

To ensure uninterrupted message delivery, make sure your wallet is sufficiently funded. Messages will not be delivered if your wallet balance is insufficient.

> 📸 *Wallet balance section*\
> ![](/files/THYUaTXFOTo6tPqcHWmu)

***

### Troubleshooting

* If OTP is not received, verify the phone number is active and has network coverage.
* If approval exceeds 24 hours, try reconnecting the number or contact support.
* Ensure the WhatsApp number is not already active on another platform.

***

✅ Setup complete! Your WhatsApp integration is now ready for use.


# Custom Events

Cuoral’s churn Intelligence System provides powerful **analytics** and **session replay** to help you understand how users interact with your website and exact point you need to engage them.

In addition to automatically tracking common interactions, you can send **custom events** to monitor important business actions and product usage.

Custom events allow you to capture **meaningful user behavior that automatic tracking cannot detect**.

***

## Automatic Tracking

Cuoral Intelligence automatically tracks the following events:

| Event Type            | Description                                              |
| --------------------- | -------------------------------------------------------- |
| **Page Views**        | Every page visit and navigation                          |
| **Clicks**            | Interactions with buttons, links, and clickable elements |
| **Scroll Depth**      | How far users scroll on each page                        |
| **Form Interactions** | Form submissions, field completion, and form abandonment |
| **API Calls**         | Network requests and responses                           |
| **Console Errors**    | JavaScript errors and unhandled exceptions               |
| **Session Replay**    | Video-like playback of complete user sessions            |

***

## Why Use Custom Events?

Custom events help you track **specific user behaviors that matter to your business**.

Common use cases include:

* Important button actions (e.g., **Add to Cart**, **Start Checkout**)
* Feature usage (e.g., **Opened Filter Menu**, **Applied Coupon Code**)
* User preferences (e.g., **Changed Language**, **Enabled Dark Mode**)
* Onboarding progress (e.g., **Completed Onboarding Step 3**)
* Product milestones (e.g., **Reached Quiz Question 5**)

***

## Prerequisites

Before sending custom events, make sure:

1. **Cuoral Intelligence is enabled** for your organization
2. **The Cuoral widget is installed** on your website
3. You have access to modify your website’s **JavaScript code**

To verify Intelligence is enabled, check:

```
Dashboard → Intelligence & Analytics
```

or contact your Cuoral administrator.

***

## Quick Start

### 1. Verify Cuoral is Loaded

Ensure the Cuoral widget is present on your website.\
You should see the **Cuoral chat icon** on the page.

***

### 2. Track Your First Custom Event

```javascript
window.Cuoral.trackCustomEvent(
  'button_clicked',   // Event name
  'user_action',      // Event category
  { page: 'home' }    // Optional properties
);
```

***

### 3. View the Event

Navigate to:

```
Dashboard → Intelligence & Analytics → Custom Events
```

Your event should appear shortly after it is sent.

***

## API Reference

### `trackCustomEvent`

```js
window.Cuoral.trackCustomEvent(
  name,
  category,
  properties,
  elementSelector,
  elementText
)
```

#### Parameters

| Parameter         | Type   | Required | Description                                             |
| ----------------- | ------ | -------- | ------------------------------------------------------- |
| `name`            | string | Yes      | Name of the event (e.g., `add_to_cart`, `video_played`) |
| `category`        | string | Yes      | Event category (e.g., `ecommerce`, `navigation`)        |
| `properties`      | object | No       | Additional data to attach to the event                  |
| `elementSelector` | string | No       | CSS selector of the triggering element                  |
| `elementText`     | string | No       | Text content of the triggering element                  |

Defaults:

```
properties = {}
elementSelector = null
elementText = null
```

***

## Automatic Event Properties

Every custom event automatically includes the following metadata:

| Property          | Description                       |
| ----------------- | --------------------------------- |
| `session_id`      | Current session identifier        |
| `url`             | Page URL where the event occurred |
| `event_timestamp` | ISO timestamp of the event        |
| `viewport_width`  | Browser viewport width            |
| `viewport_height` | Browser viewport height           |
| `screen_width`    | Device screen width               |
| `screen_height`   | Device screen height              |
| `user_agent`      | Browser user agent                |
| `referrer`        | Page referrer                     |
| `language`        | Browser language                  |

***

## Implementation Examples

### Track a Button Click

```js
document.getElementById('signup-button').addEventListener('click', () => {
  window.Cuoral.trackCustomEvent(
    'signup_clicked',
    'conversion',
    { source: 'homepage_hero' }
  );
});
```

***

### Track Form Submission

```js
document.getElementById('contact-form').addEventListener('submit', function(e) {
  e.preventDefault();

  window.Cuoral.trackCustomEvent(
    'contact_form_submitted',
    'conversion',
    {
      form_type: 'contact',
      has_phone: !!this.phone.value,
      message_length: this.message.value.length
    }
  );
});
```

***

### Track Feature Usage

```javascript
function toggleDarkMode() {
  const isDark = document.body.classList.toggle('dark-mode');

  window.Cuoral.trackCustomEvent(
    'dark_mode_toggled',
    'preferences',
    { enabled: isDark }
  );
}
```

***

## Recommended Event Categories

To keep analytics organized, use standardized categories:

| Category          | Use Case                             |
| ----------------- | ------------------------------------ |
| **ecommerce**     | Shopping, cart actions, checkout     |
| **media**         | Video or audio playback              |
| **navigation**    | Menu clicks, page transitions        |
| **engagement**    | Searches, filters, interactions      |
| **conversion**    | Signups, form submissions, downloads |
| **user\_action**  | Generic user interactions            |
| **error**         | Custom error tracking                |
| **preferences**   | Settings and user preferences        |
| **user\_journey** | Onboarding flows and tutorials       |

***

## Viewing Your Events

After implementation, events can be analyzed in the Cuoral dashboard.

#### 1. Open Analytics

```
Dashboard → Intelligence & Analytics
```

#### 2. Filter Events

Select **Custom Events** from the event type filter.

#### 3. Analyze Behavior

You can:

* Group events by **category**
* Build **conversion funnels**
* View events in **session replay**
* Track feature usage trends

***

## Testing Custom Events

To confirm events are being tracked correctly:

```js
window.Cuoral.trackCustomEvent('test_event', 'testing', {
  test_time: new Date().toISOString()
});

// Optional: force immediate send
window.Cuoral.flush();
```

Then open **Developer Tools → Network** and look for requests to:

```
customer-intelligence/session-recording/batch
```


# Sensitive Data Protection

Cuoral's session recording system includes built-in privacy features to prevent capturing sensitive customer data. This guide explains how to block specific elements from being recorded.

### Quick Start

Add the `cuoral-block` class to any HTML element containing sensitive information:

```html
<div class="cuoral-block">
  Credit Card: 1234-5678-9012-3456
</div>
```

***

### Method 1: CSS Classes (Recommended)

The easiest way to protect sensitive elements is by adding specific CSS classes directly to your HTML.

#### Block Classes

These classes completely **block elements from being recorded**:

* `cuoral-block`
* `sensitive-data`
* `confidential`

**Example Usage:**

```html
<!-- Block credit card information -->
<div class="cuoral-block">
  <label>Credit Card Number</label>
  <input type="text" name="card-number" />
</div>

<!-- Block API keys or tokens -->
<div class="sensitive-data">
  API Key: sk_live_abc123xyz789
</div>

<!-- Block confidential business data -->
<section class="confidential">
  <h3>Internal Company Metrics</h3>
  <p>Revenue: $1,234,567</p>
</section>
```

#### Ignore Classes

These classes **ignore interaction events** while still showing the element structure:

* `cuoral-ignore`
* `no-track`

```html
<!-- Don't track clicks/interactions but show element -->
<button class="cuoral-ignore">Internal Admin Action</button>
```

***

### Method 2: JavaScript API

Use the Cuoral JavaScript API to dynamically add custom blocking classes.

#### Add Custom Block Classes

```javascript
// Add a single class to the block list
Cuoral.blockClasses('my-sensitive-class');

// Add multiple classes at once
Cuoral.blockClasses(['private-info', 'internal-data', 'company-secrets']);
```

After adding custom classes, use them in your HTML:

```html
<div class="my-sensitive-class">
  This content will be blocked from recording
</div>
```

#### Add Custom Sensitive Field Patterns

Define custom patterns to automatically detect and mask sensitive input fields:

```javascript
// Add single pattern
Cuoral.addSensitivePatterns(/passport/i);

// Add multiple patterns
Cuoral.addSensitivePatterns([
  /tax[_-]?id/i,
  /national[_-]?id/i,
  /driver[_-]?license/i
]);
```

***

### Method 3: Configure Privacy Settings

Customize global privacy behavior:

```javascript
Cuoral.configurePrivacy({
  maskAllInputs: false,              // Set to false to only mask sensitive inputs
  autoMaskSensitivePatterns: true    // Auto-detect sensitive field names
});
```

#### Configuration Options:

| Option                      | Type    | Default | Description                                               |
| --------------------------- | ------- | ------- | --------------------------------------------------------- |
| `maskAllInputs`             | boolean | `true`  | Mask all input fields by default                          |
| `autoMaskSensitivePatterns` | boolean | `true`  | Automatically detect and mask fields with sensitive names |

***

### Automatic Protection

Cuoral **automatically masks** these sensitive input types without any configuration:

#### Input Types (Always Masked):

* `<input type="password">`
* `<input type="tel">`
* `<input type="email">`
* `<input type="number">`

#### Field Name Patterns (Auto-detected):

Fields with names or IDs matching these patterns are automatically masked:

* `password`
* `credit-card` or `credit_card`
* `card-number` or `card_number`
* `cvv` or `cvc`
* `ssn`
* `social-security` or `social_security`
* `routing-number` or `routing_number`
* `account-number` or `account_number`
* `pin-code` or `pin_code`
* `secret`
* `token`
* `api-key` or `api_key`

***

### Complete Example

```html
<!DOCTYPE html>
<html>
<head>
  <meta name="cuoral-public-key" content="your-public-key">
  <script src="https://cuoral.com/intelligence.js"></script>
</head>
<body>
  <!-- Regular content - will be recorded -->
  <form>
    <label>Username</label>
    <input type="text" name="username" />

    <!-- Auto-masked (password type) -->
    <label>Password</label>
    <input type="password" name="password" />

    <!-- Completely blocked from recording -->
    <div class="cuoral-block">
      <label>Credit Card Number</label>
      <input type="text" name="cc-number" />
      
      <label>CVV</label>
      <input type="text" name="cvv" />
    </div>

    <!-- Custom sensitive class -->
    <div class="my-sensitive-class">
      <label>Social Security Number</label>
      <input type="text" name="ssn" />
    </div>

    <button type="submit">Submit</button>
  </form>

  <script>
    // Add custom blocking classes
    Cuoral.blockClasses('my-sensitive-class');
    
    // Add custom sensitive patterns
    Cuoral.addSensitivePatterns([/passport/i, /driver[_-]?license/i]);
  </script>
</body>
</html>
```

***

### Best Practices

#### ✅ Do:

* Use `cuoral-block` on container elements holding sensitive data
* Add blocking classes during development, not at runtime
* Test your implementation to verify sensitive data is blocked
* Block entire form sections containing payment information
* Use semantic class names for your custom block classes

#### ❌ Don't:

* Rely solely on automatic masking for highly sensitive data
* Use blocking classes on the `<body>` or main container (blocks everything)
* Add block classes dynamically after page load (may miss initial recording)
* Forget to document which elements need protection for your team

***

### Verification

To verify that sensitive elements are properly blocked:

1. Open your browser's Developer Console
2. Enable debug mode:

   ```javascript
   Cuoral.configurePrivacy({ debugMode: true });
   ```
3. Check console logs for blocked elements and masked fields
4. Review session recordings in your Cuoral dashboard


# Mobile


# Mobile Notifications

Cuoral automatically handles **web notifications** for new messages. If you also want to notify users on your mobile application, you can use Cuoral's **Message Webhook** to build your own mobile push notification flow.

The webhook allows your backend to receive new message events and forward them to your mobile notification service.

### How It Works

The mobile notification flow works as follows:

```
Customer sends message
        ↓
Cuoral receives message
        ↓
Cuoral sends Message Webhook
        ↓
Your Backend
        ↓
Your Push Notification Service
        ↓
Mobile App
        ↓
User receives notification
```

Cuoral is responsible for sending the webhook. Your application is responsible for processing the webhook and triggering the mobile push notification.

### Web Notifications vs. Mobile Notifications

| Notification Type         | Responsibility                  |
| ------------------------- | ------------------------------- |
| Web notifications         | Handled automatically by Cuoral |
| Mobile push notifications | Handled by your application     |

You do not need to implement anything for web notifications.

For mobile notifications, you need to:

1. Receive the `message` webhook.
2. Determine whether the message should trigger a mobile notification.
3. Identify the mobile user/device that should receive the notification.
4. Send the notification through your push notification provider.

### Message Webhook

When a new message is created, Cuoral sends a `message` webhook to your configured webhook endpoint.

Example payload:

```json
{
  "email": "customer@example.com",
  "name": "John Doe",
  "message": "Hello, I need help with my account.",
  "sender": "customer"
}
```

The `sender` field indicates who sent the message:

* `customer` — The message was sent by the customer.
* `internal` — The message was sent by an agent, internal user, or bot.

See [**Message Event**](#message-webhook) for the complete webhook payload and event documentation.

### Recommended Architecture

We recommend processing mobile notifications on your backend rather than directly from the mobile application.

```
                  ┌──────────────┐
                  │    Cuoral    │
                  └──────┬───────┘
                         │
                  Message Webhook
                         │
                         ▼
                  ┌──────────────┐
                  │ Your Backend │
                  └──────┬───────┘
                         │
                Push Notification
                         │
                         ▼
               ┌──────────────────┐
               │ Push Provider    │
               │                  │
               │ APNs / FCM / ... │
               └────────┬─────────┘
                        │
                        ▼
                  ┌──────────────┐
                  │  Mobile App  │
                  └──────────────┘
```

This approach keeps your push notification credentials and business logic on your server.

### Processing a Message

When your backend receives a message webhook:

#### 1. Verify the Webhook

Before processing the event, verify the `X-Cuoral-Webhook-Signature` header.

See **Webhook Authentication** for details.

#### 2. Check the Sender

Determine whether the message was sent by a customer or an internal user.

For example:

```javascript
if (payload.sender === "customer") {
  // Consider sending a mobile notification
}
```

Depending on your application's requirements, you may only want to notify mobile users for messages sent by customers.

#### 3. Identify the Recipient

Use the information in the webhook payload, such as the customer's `email`, to determine which user or device should receive the notification.

Your application should maintain the relationship between your users and their mobile push tokens.

For example:

```
customer@example.com
        ↓
Your User ID
        ↓
Mobile Device Token
```

#### 4. Send the Push Notification

Once you have identified the recipient, send a push notification through your preferred notification provider.

Common options include:

* **Apple Push Notification service (APNs)** for iOS
* **Firebase Cloud Messaging (FCM)** for Android
* Other push notification providers that support your mobile application

The push notification might contain:

```json
{
  "title": "New message",
  "body": "Hello, I need help with my account."
}
```

### Handling Retries

Cuoral may retry a webhook if your endpoint does not return a successful response.

Because of this, your webhook handler should be **idempotent**.

Avoid sending duplicate mobile notifications if the same webhook is delivered more than once.

> **Important:** Your webhook endpoint should acknowledge the webhook only after it has been successfully received and validated. Your application should also have a strategy for preventing duplicate push notifications.

### Example Flow

A customer sends:

> "Hello, I need help with my account."

Cuoral sends your backend:

```json
{
  "email": "customer@example.com",
  "name": "John Doe",
  "message": "Hello, I need help with my account.",
  "sender": "customer"
}
```

Your backend:

1. Verifies the webhook signature.
2. Checks that `sender` is `customer`.
3. Looks up the user associated with `customer@example.com`.
4. Retrieves the user's mobile push token.
5. Sends the notification through APNs, FCM, or your push provider.
6. Returns `200 OK` to Cuoral.

The user then receives the notification in your mobile application.

### What Cuoral Handles

Cuoral automatically handles:

* Receiving the original message
* Sending the message webhook
* Web notifications

### What Your Application Handles

Your application is responsible for:

* Receiving and verifying the webhook
* Determining who should receive a mobile notification
* Managing mobile device tokens
* Sending push notifications
* Handling notification preferences
* Preventing duplicate notifications

### Next Steps

To implement mobile notifications, you will need:

1. A configured **Message Webhook** endpoint.
2. Webhook signature verification.
3. A way to map Cuoral users to your mobile users.
4. A mobile push notification provider such as APNs or FCM.
5. Logic to process message events and send notifications.


# Android Permission

## Google Play Store Screen Recording Declaration Guide

### For Apps Using the Cuoral SDK

If your app integrates the **Cuoral customer support SDK** and allows users to record their screen when reporting issues, you must declare this functionality in the **Google Play Console**.

This declaration is required because the SDK uses the `FOREGROUND_SERVICE_MEDIA_PROJECTION` permission for screen recording.

***

## Step-by-Step: Submit the Declaration

1. Open **Google Play Console**
2. Navigate to **Policy**
3. Select **App Content**
4. Find **Foreground Service Types**
5. Locate `FOREGROUND_SERVICE_MEDIA_PROJECTION`
6. Click **Manage**
7. Complete the form using the justification below

***

## Declaration Details

#### Category

**Customer Support / Bug Reporting**

#### Justification (Copy & Paste)

```
Our app integrates the Cuoral customer support SDK, which allows users to voluntarily record their screen when reporting issues or requesting assistance.

Recording only begins after the user explicitly taps the "Record" button within the support widget. A visible notification is displayed throughout the recording session, and users can stop or cancel the recording at any time.

The recordings are used exclusively for customer support and bug reporting purposes to help our support team better understand and resolve user issues.

No screen recording occurs automatically or in the background. All recordings are securely encrypted and transmitted to our support backend for analysis.
```

***

## Privacy Policy Requirement

Your **privacy policy must clearly disclose** that the app allows users to record their screen for support purposes.

Add a section explaining:

* Screen recording is **optional and user-initiated**
* Recording only starts when the **user explicitly activates it**
* Recordings are used **only for customer support and troubleshooting**
* Recordings are **securely transmitted and stored**

***

## Important Notes

* No code changes are required.
* This is **only a Play Console declaration**.
* The feature must remain **user-initiated and visible** to comply with Google Play policies.

***


# Newsletter Subscription

Collect email subscribers directly from your website with beautiful, ready-to-use UI components or a simple programmatic API.

### Installation

The newsletter module is included in `inline.js`. No additional scripts needed — it's available automatically when you load the Cuoral widget:

```html
<script
  src="https://js.cuoral.com/inline.js"
  data-cuoral-key="your-public-key"
></script>
```

The `CuoralNewsletter` object is then globally available.

### Method 1: Modal Popup

Show a beautiful animated modal to collect subscriptions. Great for exit-intent, button clicks, or timed popups.

```javascript
CuoralNewsletter.showModal({
  title: "Stay in the loop",
  subtitle: "Get the latest updates delivered to your inbox.",
  buttonText: "Subscribe",
  color: "#6366f1",
  showName: true,
  showConsent: true,
  consentText: "I agree to receive emails and can unsubscribe anytime.",
  successTitle: "You're subscribed! 🎉",
  successMessage: "Check your inbox for confirmation.",
  tags: ["website-popup"],
  onSuccess: (data) => {
    console.log("Subscribed!", data);
  },
  onError: (error) => {
    console.error("Failed:", error);
  },
  onClose: () => {
    console.log("Modal closed");
  },
});
```

#### Modal Options

| Parameter        | Type      | Default                       | Description                      |
| ---------------- | --------- | ----------------------------- | -------------------------------- |
| `title`          | string    | `"Stay in the loop"`          | Modal heading                    |
| `subtitle`       | string    | `"Get the latest updates..."` | Description text                 |
| `buttonText`     | string    | `"Subscribe"`                 | Submit button label              |
| `color`          | string    | `"#6366f1"`                   | Brand/theme color (hex)          |
| `showName`       | boolean   | `false`                       | Show first & last name fields    |
| `showConsent`    | boolean   | `true`                        | Show consent checkbox            |
| `consentText`    | string    | `"I agree to receive..."`     | Custom consent label             |
| `successTitle`   | string    | `"You're subscribed! 🎉"`     | Title after success              |
| `successMessage` | string    | `"Thanks for subscribing..."` | Message after success            |
| `tags`           | string\[] | `[]`                          | Tags for subscriber segmentation |
| `onSuccess`      | function  | `null`                        | Callback with subscriber data    |
| `onError`        | function  | `null`                        | Callback with error details      |
| `onClose`        | function  | `null`                        | Callback when modal is closed    |

### Method 2: Inline Form Embed

Embed a form directly into any element on your page. Perfect for footers, sidebars, or dedicated signup sections.

```javascript
CuoralNewsletter.embed("#newsletter-container", {
  title: "Subscribe to our newsletter",
  subtitle: "Stay updated with the latest news.",
  layout: "inline",
  color: "#6366f1",
  buttonText: "Subscribe",
  showName: false,
  tags: ["footer-form"],
  onSuccess: (data) => console.log("Subscribed!", data),
  onError: (error) => console.error("Error:", error),
});
```

#### Embed Options

| Parameter          | Type              | Default                         | Description                      |
| ------------------ | ----------------- | ------------------------------- | -------------------------------- |
| `target` (1st arg) | string \| Element | —                               | CSS selector or DOM element      |
| `title`            | string            | `"Subscribe to our newsletter"` | Form heading                     |
| `subtitle`         | string            | `"Stay updated..."`             | Description text                 |
| `buttonText`       | string            | `"Subscribe"`                   | Submit button label              |
| `color`            | string            | `"#6366f1"`                     | Brand/theme color (hex)          |
| `showName`         | boolean           | `false`                         | Show first & last name fields    |
| `layout`           | string            | `"stacked"`                     | `"stacked"` or `"inline"`        |
| `tags`             | string\[]         | `[]`                            | Tags for subscriber segmentation |
| `onSuccess`        | function          | `null`                          | Callback with subscriber data    |
| `onError`          | function          | `null`                          | Callback with error details      |

#### Layout Options

* **`"inline"`** — Email input and button on the same row (compact, great for headers/footers)
* **`"stacked"`** — Fields stacked vertically (traditional form layout)

> **Note:** Inline layout only works when `showName` is `false`. If name fields are enabled, it falls back to stacked.

### Method 3: Programmatic Subscribe

For custom forms where you handle the UI yourself. Just pass the email and get the result.

```javascript
// Inside your own form submit handler
const result = await CuoralNewsletter.subscribe({
  email: "user@example.com",
  firstName: "John",        // optional
  lastName: "Doe",          // optional
  tags: ["custom-form"],    // optional
  customFields: {           // optional
    company: "Acme Inc",
    role: "Developer",
  },
});

if (result.status) {
  // Success!
  console.log(result.data);
  // { email: "user@example.com", contact_uid: "...", is_subscribed: true }
} else {
  // Error
  console.error(result.message);
}
```

#### Subscribe Options

| Parameter      | Type      | Required | Description                |
| -------------- | --------- | -------- | -------------------------- |
| `email`        | string    | ✅ Yes    | Subscriber's email address |
| `firstName`    | string    | No       | First name                 |
| `lastName`     | string    | No       | Last name                  |
| `tags`         | string\[] | No       | Tags for segmentation      |
| `customFields` | object    | No       | Custom key-value metadata  |

#### Response Format

```json
{
  "status": true,
  "message": "Successfully subscribed",
  "data": {
    "email": "user@example.com",
    "contact_uid": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "is_subscribed": true,
    "subscription_state": "active",
    "requires_confirmation": false
  }
}
```

### Examples

#### Trigger modal on button click

```html
<button onclick="CuoralNewsletter.showModal({ color: '#e11d48' })">
  Subscribe Now
</button>
```

#### Embed in footer

```html
<div id="footer-newsletter"></div>
<script>
  CuoralNewsletter.embed("#footer-newsletter", {
    layout: "inline",
    title: "",
    subtitle: "",
    buttonText: "→",
    color: "#1e293b",
  });
</script>
```

#### Exit-intent popup

```javascript
let shown = false;
document.addEventListener("mouseout", (e) => {
  if (e.clientY < 10 && !shown) {
    shown = true;
    CuoralNewsletter.showModal({
      title: "Wait! Don't leave yet 👋",
      subtitle: "Subscribe for a 10% discount on your first purchase.",
      color: "#059669",
      tags: ["exit-intent"],
    });
  }
});
```

#### Custom form integration

```html
<form id="my-form">
  <input type="email" id="my-email" placeholder="Enter email" />
  <button type="submit">Subscribe</button>
</form>

<script>
document.getElementById("my-form").addEventListener("submit", async (e) => {
  e.preventDefault();
  const email = document.getElementById("my-email").value;

  const result = await CuoralNewsletter.subscribe({ email });

  if (result.status) {
    alert("Subscribed successfully!");
  } else {
    alert("Error: " + result.message);
  }
});
</script>
```

### Notes

* **`org_identifier`** is automatically set from your `data-cuoral-key` attribute.
* **`source_details`** auto-captures the current page URL where the subscription happened.
* **`consent`** is auto-generated with a timestamp when the user subscribes.
* The modal auto-closes 4 seconds after successful subscription.
* All forms include built-in email validation.
* Responsive design works on all screen sizes.
* Demo URL: <https://js.cuoral.com/newsletter-demo.html>


# Customers

{% content-ref url="/pages/YyCFy43yTQj1dulv63Gd" %}
[Broken mention](broken://pages/YyCFy43yTQj1dulv63Gd)
{% endcontent-ref %}


# Contact


# Get all contacts

Returns a paginated list of customers. Supports filtering, search, and sorting.

### **Endpoint**

```http
GET /external/v1/customers
```

### **Query Parameters**

| Name         | Type          | Description                     | Default        | Constraints      |
| ------------ | ------------- | ------------------------------- | -------------- | ---------------- |
| `page`       | integer       | Page number                     | `1`            | min: 1           |
| `page_size`  | integer       | Items per page                  | `20`           | min: 1, max: 100 |
| `search`     | string        | Search in name, email, or phone | —              | optional         |
| `source`     | string        | Filter by customer source       | —              | optional         |
| `company_id` | string (UUID) | Filter by company ID            | —              | optional         |
| `is_active`  | boolean       | Filter by active status         | —              | optional         |
| `sort_by`    | string        | Field to sort by                | `time_created` | optional         |
| `sort_order` | string        | Sort order (`asc`, `desc`)      | `desc`         | optional         |

***

### **Response — 200 OK**

```json
{
  "data": [
    {
      "name": "string",
      "email": "user@example.com",
      "whatsapp_number": "string",
      "source": "string",
      "ip": "string",
      "city": "string",
      "country": "string",
      "location": "string",
      "latitude": "string",
      "longitude": "string",
      "is_active": true,
      "id": 0,
      "customer_id": "string",
      "time_created": "2025-11-27T09:32:08.249Z",
      "time_updated": "2025-11-27T09:32:08.249Z",
      "organization_id": 0,
      "company_id": 0,
      "company_name": "string"
    }
  ],
  "total": 0,
  "page": 0,
  "page_size": 0,
  "total_pages": 0,
  "has_next": true,
  "has_previous": true
}
```

***

### **Response Fields**

| Field          | Type    | Description                        |
| -------------- | ------- | ---------------------------------- |
| `data`         | array   | List of customers                  |
| `total`        | integer | Total number of matching customers |
| `page`         | integer | Current page                       |
| `page_size`    | integer | Number of items per page           |
| `total_pages`  | integer | Total number of pages              |
| `has_next`     | boolean | Whether a next page exists         |
| `has_previous` | boolean | Whether a previous page exists     |

#### **Customer Object**

| Field             | Type                  | Description                    |
| ----------------- | --------------------- | ------------------------------ |
| `id`              | integer               | Internal ID                    |
| `customer_id`     | string                | Public customer ID             |
| `name`            | string                | Customer name                  |
| `email`           | string                | Email address                  |
| `whatsapp_number` | string                | WhatsApp number                |
| `source`          | string                | Customer source                |
| `ip`              | string                | IP address                     |
| `city`            | string                | City                           |
| `country`         | string                | Country                        |
| `location`        | string                | Full location                  |
| `latitude`        | string                | Latitude                       |
| `longitude`       | string                | Longitude                      |
| `is_active`       | boolean               | Whether the customer is active |
| `organization_id` | integer               | Organization ID                |
| `company_id`      | integer               | Company ID                     |
| `company_name`    | string                | Company name                   |
| `time_created`    | string (ISO datetime) | Creation timestamp             |
| `time_updated`    | string (ISO datetime) | Update timestamp               |

***

### **Response — 422 Validation Error**

```json
{
  "detail": [
    {
      "loc": ["string", 0],
      "msg": "string",
      "type": "string"
    }
  ]
}
```


# Get Customer By Email

Get a specific customer by email

### **Endpoint**

```http
GET /external/v1/customers/by-email/{email}
```

### **Response - 200 OK**

```json
{
  "name": "string",
  "email": "user@example.com",
  "whatsapp_number": "string",
  "source": "string",
  "ip": "string",
  "city": "string",
  "country": "string",
  "location": "string",
  "latitude": "string",
  "longitude": "string",
  "is_active": true,
  "customer_id": "string",
  "time_created": "2026-08-07T09:07:26.504Z",
  "time_updated": "2026-08-07T09:07:26.504Z",
  "organization_id": 0,
  "company_id": 0,
  "last_session_id": "string",
  "last_session_node": "string",
  "last_session_node_at": "2026-08-07T09:07:26.504Z",
  "last_session_channel": "string"
}
```

***

### **Response Fields**

| Field             | Type                  | Description                    |
| ----------------- | --------------------- | ------------------------------ |
| `id`              | integer               | Internal ID                    |
| `customer_id`     | string                | Public customer ID             |
| `name`            | string                | Customer name                  |
| `email`           | string                | Email address                  |
| `whatsapp_number` | string                | WhatsApp number                |
| `source`          | string                | Customer source                |
| `ip`              | string                | IP address                     |
| `city`            | string                | City                           |
| `country`         | string                | Country                        |
| `location`        | string                | Full location                  |
| `latitude`        | string                | Latitude                       |
| `longitude`       | string                | Longitude                      |
| `is_active`       | boolean               | Whether the customer is active |
| `organization_id` | integer               | Organization ID                |
| `company_id`      | integer               | Company ID                     |
| `company_name`    | string                | Company name                   |
| `time_created`    | string (ISO datetime) | Creation timestamp             |
| `time_updated`    | string (ISO datetime) | Update timestamp               |

***

### **Response — 422 Validation Error**

```json
{
  "detail": [
    {
      "loc": ["string", 0],
      "msg": "string",
      "type": "string"
    }
  ]
}
```


# Get Customer By ID

Get a specific customer by email

### **Endpoint**

```http
GET /external/v1/customers/{customer_id}
```

### **Response - 200 OK**

```json
{
  "name": "string",
  "email": "user@example.com",
  "whatsapp_number": "string",
  "source": "string",
  "ip": "string",
  "city": "string",
  "country": "string",
  "location": "string",
  "latitude": "string",
  "longitude": "string",
  "is_active": true,
  "customer_id": "string",
  "time_created": "2026-08-07T09:15:57.799Z",
  "time_updated": "2026-08-07T09:15:57.799Z",
  "organization_id": 0,
  "company_id": 0,
  "last_session_id": "string",
  "last_session_node": "string",
  "last_session_node_at": "2026-08-07T09:15:57.799Z",
  "last_session_channel": "string"
}
```

***

### **Response Fields**

| Field             | Type                  | Description                    |
| ----------------- | --------------------- | ------------------------------ |
| `id`              | integer               | Internal ID                    |
| `customer_id`     | string                | Public customer ID             |
| `name`            | string                | Customer name                  |
| `email`           | string                | Email address                  |
| `whatsapp_number` | string                | WhatsApp number                |
| `source`          | string                | Customer source                |
| `ip`              | string                | IP address                     |
| `city`            | string                | City                           |
| `country`         | string                | Country                        |
| `location`        | string                | Full location                  |
| `latitude`        | string                | Latitude                       |
| `longitude`       | string                | Longitude                      |
| `is_active`       | boolean               | Whether the customer is active |
| `organization_id` | integer               | Organization ID                |
| `company_id`      | integer               | Company ID                     |
| `company_name`    | string                | Company name                   |
| `time_created`    | string (ISO datetime) | Creation timestamp             |
| `time_updated`    | string (ISO datetime) | Update timestamp               |

***

### **Response — 422 Validation Error**

```json
{
  "detail": [
    {
      "loc": ["string", 0],
      "msg": "string",
      "type": "string"
    }
  ]
}
```


# Bulk Create Customers

Bulk create customers (up to 100 at a time).

**POST** `/external/v1/external/v1/customers/bulk`

Bulk create customers (up to 100 at a time).

#### Parameters

* None

#### Request Body

The request body is a JSON object containing an array of customer objects to be created.

**Content Type:** `application/json`

| Name          | Type             | Description                             |
| ------------- | ---------------- | --------------------------------------- |
| **customers** | Array of Objects | An array of up to 100 customer objects. |

**Customer Object Fields:**

| Field                | Type      | Description                                            | Example              |
| -------------------- | --------- | ------------------------------------------------------ | -------------------- |
| **name**             | `string`  | The customer's full name.                              | `"Jane Doe"`         |
| **email**            | `string`  | The customer's email address.                          | `"user@example.com"` |
| **whatsapp\_number** | `string`  | The customer's WhatsApp contact number.                | `"2348000000000"`    |
| **source**           | `string`  | The origin of the customer data (e.g., lead source).   | `"website_signup"`   |
| **ip**               | `string`  | The customer's IP address.                             | `"192.168.1.1"`      |
| **city**             | `string`  | The city where the customer is located.                | `"Lagos"`            |
| **country**          | `string`  | The country where the customer is located.             | `"Nigeria"`          |
| **location**         | `string`  | A general location description.                        | `"Office Block A"`   |
| **latitude**         | `string`  | The geographical latitude of the customer's location.  | `"6.5244"`           |
| **longitude**        | `string`  | The geographical longitude of the customer's location. | `"3.3792"`           |
| **is\_active**       | `boolean` | Indicates if the customer account is currently active. | `true`               |
| **company\_id**      | `string`  | The ID of the company the customer is associated with. | `"CMP-12345"`        |

**Example Request Body:**

```json
{
  "customers": [
    {
      "name": "Jane Doe",
      "email": "jane.doe@example.com",
      "whatsapp_number": "2348000000000",
      "source": "website_signup",
      "ip": "192.168.1.1",
      "city": "Lagos",
      "country": "Nigeria",
      "location": "Lekki Phase 1",
      "latitude": "6.45407",
      "longitude": "3.38467",
      "is_active": true,
      "company_id": "CMP-12345"
    },
    {
      "name": "John Smith",
      "email": "john.smith@example.com",
      "whatsapp_number": "2348011111111",
      "source": "referral",
      "ip": "10.0.0.5",
      "city": "Abuja",
      "country": "Nigeria",
      "location": "Wuse 2",
      "latitude": "9.05786",
      "longitude": "7.49508",
      "is_active": true,
      "company_id": "CMP-67890"
    }
  ]
}
```

### Responses

#### 201 Successful Response (Created)

Returns the count of customers created and the list of created customer objects, including generated IDs and timestamps.

Content Type: `application/json`

| **Field** | **Type**         | **Description**                                     |
| --------- | ---------------- | --------------------------------------------------- |
| created   | `integer`        | The total number of customers successfully created. |
| customers | Array of Objects | A list of the newly created customer objects.       |

Created Customer Object Fields:

In addition to the request fields, the response customer objects include:

| **Field**        | **Type**            | **Description**                                                 |
| ---------------- | ------------------- | --------------------------------------------------------------- |
| id               | `integer`           | The internal database ID of the customer.                       |
| customer\_id     | `string`            | The unique external ID assigned to the customer.                |
| time\_created    | `string` (datetime) | The timestamp when the customer record was created.             |
| time\_updated    | `string` (datetime) | The timestamp when the customer record was last updated.        |
| organization\_id | `integer`           | The organization ID the customer belongs to.                    |
| company\_id      | `integer`           | The company ID the customer is associated with (as an integer). |

Example Response Body (201):

JSON

```json
{
  "created": 2,
  "customers": [
    {
      "name": "Jane Doe",
      "email": "jane.doe@example.com",
      "whatsapp_number": "2348000000000",
      "source": "website_signup",
      "ip": "192.168.1.1",
      "city": "Lagos",
      "country": "Nigeria",
      "location": "Lekki Phase 1",
      "latitude": "6.45407",
      "longitude": "3.38467",
      "is_active": true,
      "id": 101,
      "customer_id": "CUST-A1B2C3",
      "time_created": "2025-11-27T09:36:31.355Z",
      "time_updated": "2025-11-27T09:36:31.355Z",
      "organization_id": 42,
      "company_id": 5
    },
    {
      "name": "John Smith",
      "email": "john.smith@example.com",
      "whatsapp_number": "2348011111111",
      "source": "referral",
      "ip": "10.0.0.5",
      "city": "Abuja",
      "country": "Nigeria",
      "location": "Wuse 2",
      "latitude": "9.05786",
      "longitude": "7.49508",
      "is_active": true,
      "id": 102,
      "customer_id": "CUST-D4E5F6",
      "time_created": "2025-11-27T09:36:32.100Z",
      "time_updated": "2025-11-27T09:36:32.100Z",
      "organization_id": 42,
      "company_id": 10
    }
  ]
}
```

#### 422 Validation Error

Returned if the request body is malformed or contains invalid data (e.g., missing required fields, invalid data types).

Content Type: `application/json`

| **Field** | **Type**         | **Description**                     |
| --------- | ---------------- | ----------------------------------- |
| detail    | Array of Objects | A list of validation error objects. |

Error Detail Object Fields:

| **Field** | **Type**                      | **Description**                                                                        |
| --------- | ----------------------------- | -------------------------------------------------------------------------------------- |
| loc       | Array of `string` / `integer` | Location of the error in the request body (e.g., `["body", "customers", 0, "email"]`). |
| msg       | `string`                      | A description of the validation error.                                                 |
| type      | `string`                      | The type of validation error (e.g., `value_error.missing`).                            |

Example Response Body (422):

JSON

```json
{
  "detail": [
    {
      "loc": [
        "body",
        "customers",
        0,
        "name"
      ],
      "msg": "Field required",
      "type": "value_error.missing"
    }
  ]
}
```


# Create Customer

Create a new individual customer record

**POST** `/external/v1/external/v1/customers`

### Request

#### Parameters

* None

#### Request Body

The request body is a JSON object defining the new customer's details.

**Content Type:** `application/json`

| Field                | Type      | Description                                                         | Example              | Required |
| -------------------- | --------- | ------------------------------------------------------------------- | -------------------- | -------- |
| **name**             | `string`  | The customer's full name.                                           | `"Alice Johnson"`    | **Yes**  |
| **email**            | `string`  | The customer's email address.                                       | `"user@example.com"` | **Yes**  |
| **whatsapp\_number** | `string`  | The customer's WhatsApp contact number.                             | `"2348000000000"`    | No       |
| **source**           | `string`  | Customer acquisition source (e.g., `'web'`, `'api'`, `'whatsapp'`). | `"web"`              | **Yes**  |
| **ip**               | `string`  | The customer's IP address.                                          | `"192.168.1.1"`      | No       |
| **city**             | `string`  | The city where the customer is located.                             | `"Accra"`            | No       |
| **country**          | `string`  | The country where the customer is located.                          | `"Ghana"`            | No       |
| **location**         | `string`  | A general location description.                                     | `"Spintex Road"`     | No       |
| **latitude**         | `string`  | Geographical latitude.                                              | `"5.6037"`           | No       |
| **longitude**        | `string`  | Geographical longitude.                                             | `"0.1870"`           | No       |
| **is\_active**       | `boolean` | Indicates if the customer account is active.                        | `true`               | No       |
| **company\_id**      | `string`  | The ID of the company the customer is associated with.              | `"CMP-A9B8"`         | No       |

**Example Request Body:**

```json
{
  "name": "Alice Johnson",
  "email": "alice.johnson@example.com",
  "whatsapp_number": "2348000000000",
  "source": "api",
  "ip": "10.0.0.2",
  "city": "Accra",
  "country": "Ghana",
  "is_active": true,
  "company_id": "CMP-A9B8"
}
```

***

### Responses

#### 201 Successful Response (Created)

Returns the newly created customer object, including generated IDs and timestamps.

Content Type: `application/json`

| **Field**          | **Type**            | **Description**                                                 |
| ------------------ | ------------------- | --------------------------------------------------------------- |
| id                 | `integer`           | The internal database ID of the customer.                       |
| customer\_id       | `string`            | The unique external ID assigned to the customer.                |
| time\_created      | `string` (datetime) | The timestamp when the customer record was created.             |
| time\_updated      | `string` (datetime) | The timestamp when the customer record was last updated.        |
| organization\_id   | `integer`           | The organization ID the customer belongs to.                    |
| company\_id        | `integer`           | The company ID the customer is associated with (as an integer). |
| *... other fields* | *...*               | *All fields from the request body are also returned.*           |

Example Response Body (201):

JSON

```json
{
  "name": "Alice Johnson",
  "email": "alice.johnson@example.com",
  "whatsapp_number": "2348000000000",
  "source": "api",
  "ip": "10.0.0.2",
  "city": "Accra",
  "country": "Ghana",
  "location": "string",
  "latitude": "string",
  "longitude": "string",
  "is_active": true,
  "id": 201,
  "customer_id": "CUST-F7E6D5",
  "time_created": "2025-11-27T09:40:04.612Z",
  "time_updated": "2025-11-27T09:40:04.612Z",
  "organization_id": 42,
  "company_id": 15
}
```

#### 422 Validation Error

Returned if the request body is malformed or contains invalid data.

Content Type: `application/json`

| **Field** | **Type**         | **Description**                     |
| --------- | ---------------- | ----------------------------------- |
| detail    | Array of Objects | A list of validation error objects. |

Example Response Body (422):

JSON

```json
{
  "detail": [
    {
      "loc": [
        "body",
        "email"
      ],
      "msg": "value is not a valid email address",
      "type": "value_error.email"
    }
  ]
}
```


# Company


# Create Company

Create a new company record associated with a customer base.

**POST** `/external/v1/external/v1/customers/companies`

### Request

#### Parameters

* None

#### Request Body

The request body is a JSON object containing the new company's details.

**Content Type:** `application/json`

| Field               | Type     | Description                                         | Example                        | Required |
| ------------------- | -------- | --------------------------------------------------- | ------------------------------ | -------- |
| **company\_name**   | `string` | The official name of the company.                   | `"Acme Corp Ltd"`              | **Yes**  |
| **company\_email**  | `string` | A primary email address for the company.            | `"info@acmecorp.com"`          | **Yes**  |
| **company\_status** | `string` | The operational status of the company.              | `"active"`                     | No       |
| **description**     | `string` | A brief description of the company or its business. | `"Software development firm."` | No       |
| **industry**        | `string` | The industry the company operates in.               | `"Technology"`                 | No       |
| **website**         | `string` | The company's main website URL.                     | `"https://www.acmecorp.com"`   | No       |
| **phone**           | `string` | The company's primary phone number.                 | `"1-555-123-4567"`             | No       |
| **address**         | `string` | The company's physical street address.              | `"123 Corporate Ave"`          | No       |

**Example Request Body:**

```json
{
  "company_name": "Acme Corp Ltd",
  "company_email": "info@acmecorp.com",
  "company_status": "active",
  "description": "Software development firm.",
  "industry": "Technology",
  "website": "[https://www.acmecorp.com](https://www.acmecorp.com)",
  "phone": "1-555-123-4567",
  "address": "123 Corporate Ave, City, Country"
}
```

### Responses

#### 201 Successful Response (Created)

Returns the newly created company object, including system-generated IDs and timestamps.

Content Type: `application/json`

| **Field**          | **Type**            | **Description**                                         |
| ------------------ | ------------------- | ------------------------------------------------------- |
| id                 | `integer`           | The internal database ID of the company.                |
| company\_id        | `string`            | The unique external ID assigned to the company.         |
| time\_created      | `string` (datetime) | The timestamp when the company record was created.      |
| time\_updated      | `string` (datetime) | The timestamp when the company record was last updated. |
| organization\_id   | `integer`           | The organization ID this company belongs to.            |
| *... other fields* | *...*               | *All fields from the request body are also returned.*   |

Example Response Body (201):

```json
{
  "company_name": "Acme Corp Ltd",
  "company_email": "info@acmecorp.com",
  "company_status": "active",
  "description": "Software development firm.",
  "industry": "Technology",
  "website": "[https://www.acmecorp.com](https://www.acmecorp.com)",
  "phone": "1-555-123-4567",
  "address": "123 Corporate Ave, City, Country",
  "id": 501,
  "company_id": "COMP-XZY-789",
  "time_created": "2025-11-27T09:44:56.631Z",
  "time_updated": "2025-11-27T09:44:56.631Z",
  "organization_id": 42
}
```

#### 422 Validation Error

Returned if the request body is malformed or contains invalid data (e.g., missing required fields).

Content Type: `application/json`

| **Field** | **Type**         | **Description**                     |
| --------- | ---------------- | ----------------------------------- |
| detail    | Array of Objects | A list of validation error objects. |

Example Response Body (422):

```json
{
  "detail": [
    {
      "loc": [
        "body",
        "company_name"
      ],
      "msg": "Field required",
      "type": "value_error.missing"
    }
  ]
}
```


# List Companies

GET /external/v1/external/v1/customers/companies

List companies with pagination, filtering, and search capabilities.

Parameters (Query)

| **Name**    | **Type** | **Description**                    | **Default**   | **Constraints / Values**                            |
| ----------- | -------- | ---------------------------------- | ------------- | --------------------------------------------------- |
| page        | integer  | Page number for pagination.        | 1             | Minimum: 1                                          |
| page\_size  | integer  | Items per page.                    | 20            | Minimum: 1, Maximum: 100                            |
| search      | string   | Search in name, email, or website. | null          |                                                     |
| status      | string   | Filter by company status.          | null          | active, prospect, on\_hold, churned, lead, inactive |
| industry    | string   | Filter by industry.                | null          |                                                     |
| sort\_by    | string   | Field to sort by.                  | time\_created |                                                     |
| sort\_order | string   | Sort order.                        | desc          | asc, desc                                           |

Successful Response (200)

```json
{
  "data": [
    {
      "company_name": "string",
      "company_email": "user@example.com",
      "company_status": "active",
      "description": "string",
      "industry": "string",
      "website": "string",
      "phone": "string",
      "address": "string",
      "id": 0,
      "company_id": "string",
      "time_created": "2025-11-27T09:46:28.066Z",
      "time_updated": "2025-11-27T09:46:28.066Z",
      "organization_id": 0
    }
  ],
  "total": 0,
  "page": 0,
  "page_size": 0,
  "total_pages": 0,
  "has_next": true,
  "has_previous": true
}
```

Validation Error (422)

JSON

```json
{
  "detail": [
    {
      "loc": [
        "string",
        0
      ],
      "msg": "string",
      "type": "string"
    }
  ]
}
```


# Bulk Create Companies

Bulk create companies (up to 50 at a time).

POST /external/v1/external/v1/customers/companies/bulk&#x20;

Parameters

* None

Request Body (application/json)

The request body is a JSON object containing an array of company objects.

| **Field** | **Type**         | **Description**                                 | **Required** |
| --------- | ---------------- | ----------------------------------------------- | ------------ |
| companies | Array of Objects | An array of up to 50 company objects to create. | Yes          |

Company Object Fields:

| **Field**       | **Type** | **Description**                            |
| --------------- | -------- | ------------------------------------------ |
| company\_name   | `string` | The official name of the company.          |
| company\_email  | `string` | A primary email address for the company.   |
| company\_status | `string` | The operational status (e.g., `'active'`). |
| description     | `string` | A brief description.                       |
| industry        | `string` | The company's industry.                    |
| website         | `string` | The company's main website URL.            |
| phone           | `string` | The company's primary phone number.        |
| address         | `string` | The company's physical street address.     |

Example Request Body:

```json
{
  "companies": [
    {
      "company_name": "Acme Corp",
      "company_email": "info@acme.com",
      "company_status": "active",
      "description": "Manufacturing",
      "industry": "Manufacturing",
      "website": "https://www.acme.com",
      "phone": "555-1212",
      "address": "1 Main St"
    }
  ]
}
```

Successful Response (201) (application/json)

| **Field** | **Type**         | **Description**                              |
| --------- | ---------------- | -------------------------------------------- |
| created   | `integer`        | The count of companies successfully created. |
| companies | Array of Objects | The list of newly created company objects.   |

Created Company Object Fields (Includes all request fields plus):

| **Field**        | **Type**            | **Description**           |
| ---------------- | ------------------- | ------------------------- |
| id               | `integer`           | The internal database ID. |
| company\_id      | `string`            | The unique external ID.   |
| time\_created    | `string` (datetime) | Creation timestamp.       |
| time\_updated    | `string` (datetime) | Last update timestamp.    |
| organization\_id | `integer`           | The organization ID.      |

Example Response Body (201):

JSON

```json
{
  "created": 1,
  "companies": [
    {
      "company_name": "string",
      "company_email": "user@example.com",
      "company_status": "active",
      "description": "string",
      "industry": "string",
      "website": "string",
      "phone": "string",
      "address": "string",
      "id": 0,
      "company_id": "string",
      "time_created": "2025-11-27T09:49:41.568Z",
      "time_updated": "2025-11-27T09:49:41.568Z",
      "organization_id": 0
    }
  ]
}
```

Validation Error (422) (application/json)

```json
{
  "detail": [
    {
      "loc": [
        "string",
        0
      ],
      "msg": "string",
      "type": "string"
    }
  ]
}
```


# Metrics


# Set Company Metric Value

Set or update a metric value for a specific company.

POST /external/v1/external/v1/customers/companies/{company\_id}/metrics&#x20;

### Request ⚙️

#### Parameters

| **Name**    | **Type** | **Description**                                      | **Location** | **Required** |
| ----------- | -------- | ---------------------------------------------------- | ------------ | ------------ |
| company\_id | `string` | Company ID (UUID) for which the metric is being set. | Path         | Yes          |

#### Request Body (application/json)

| **Field**   | **Type** | **Description**                                                         | **Example**       | **Required** |
| ----------- | -------- | ----------------------------------------------------------------------- | ----------------- | ------------ |
| metric\_id  | `string` | The unique ID of the metric being updated (e.g., `'revenue'`, `'ARR'`). | `"ARR_METRIC_ID"` | Yes          |
| company\_id | `string` | The ID of the company (redundant, but often required in the body).      | `"COMP-F7E6D5"`   | Yes          |
| value       | `string` | The value to set for the metric.                                        | `"125000.50"`     | Yes          |

Example Request Body:

```json
{
  "metric_id": "monthly_users",
  "company_id": "COMP-F7E6D5",
  "value": "9500"
}
```

***

### Responses&#x20;

#### Successful Response (201) (application/json)

Returns the created or updated metric record.

| **Field**     | **Type**            | **Description**                                                       |
| ------------- | ------------------- | --------------------------------------------------------------------- |
| id            | `integer`           | The internal database ID of the metric entry.                         |
| company\_id   | `integer`           | The ID of the company (as an integer).                                |
| metric\_id    | `integer`           | The internal ID of the metric type.                                   |
| metric\_name  | `string`            | The human-readable name of the metric.                                |
| data\_type    | `string`            | The data type of the stored value (e.g., `STRING`, `NUMBER`, `DATE`). |
| value         | `string`            | The value that was set.                                               |
| time\_created | `string` (datetime) | Creation timestamp.                                                   |
| time\_updated | `string` (datetime) | Last update timestamp.                                                |

Example Response Body (201):

JSON

```json
{
  "id": 101,
  "company_id": 501,
  "metric_id": 12,
  "metric_name": "Monthly Active Users",
  "data_type": "NUMBER",
  "value": "9500",
  "time_created": "2025-11-27T09:52:39.743Z",
  "time_updated": "2025-11-27T09:52:39.743Z"
}
```

#### Validation Error (422)&#x20;

JSON

```json
{
  "detail": [
    {
      "loc": [
        "string",
        0
      ],
      "msg": "string",
      "type": "string"
    }
  ]
}
```


# Get Company Metric Values

Get all metric values recorded for a specific company.

GET /external/v1/external/v1/customers/companies/{company\_id}/metrics&#x20;

### Request&#x20;

#### Parameters

| **Name**    | **Type** | **Description**                            | **Location** | **Required** |
| ----------- | -------- | ------------------------------------------ | ------------ | ------------ |
| company\_id | `string` | Company ID (UUID) to retrieve metrics for. | Path         | Yes          |

Example Request URL:

```
/external/v1/external/v1/customers/companies/COMP-XYZ-789/metrics
```

***

### Responses&#x20;

#### Successful Response (200) (application/json)

Returns an array of metric value objects for the specified company.

| **Field**     | **Type**            | **Description**                                                |
| ------------- | ------------------- | -------------------------------------------------------------- |
| id            | `integer`           | The internal database ID of the metric entry.                  |
| company\_id   | `integer`           | The ID of the company (as an integer).                         |
| metric\_id    | `integer`           | The internal ID of the metric type.                            |
| metric\_name  | `string`            | The human-readable name of the metric (e.g., 'Monthly Users'). |
| data\_type    | `string`            | The data type of the stored value (e.g., `STRING`, `NUMBER`).  |
| value         | `string`            | The recorded value of the metric.                              |
| time\_created | `string` (datetime) | Creation timestamp of the metric entry.                        |
| time\_updated | `string` (datetime) | Last update timestamp of the metric entry.                     |

Example Response Body (200):

JSON

```json
[
  {
    "id": 101,
    "company_id": 501,
    "metric_id": 12,
    "metric_name": "Monthly Active Users",
    "data_type": "NUMBER",
    "value": "9500",
    "time_created": "2025-11-20T10:00:00.000Z",
    "time_updated": "2025-11-27T10:03:16.608Z"
  },
  {
    "id": 102,
    "company_id": 501,
    "metric_id": 5,
    "metric_name": "Last Contact Date",
    "data_type": "STRING",
    "value": "2025-11-25",
    "time_created": "2025-01-01T00:00:00.000Z",
    "time_updated": "2025-11-25T15:30:00.000Z"
  }
]
```

#### Validation Error (422) (application/json)

```json
{
  "detail": [
    {
      "loc": [
        "string",
        0
      ],
      "msg": "string",
      "type": "string"
    }
  ]
}
```


# Metrics


# Create Metric Definition

Create a new metric definition for the organization. This metric can then be assigned values for specific companies.

POST /external/v1/external/v1/customers/metrics&#x20;

### Request&#x20;

#### Parameters

* None

#### Request Body (application/json)

| **Field**   | **Type** | **Description**                                                         | **Example**                          | **Required** |
| ----------- | -------- | ----------------------------------------------------------------------- | ------------------------------------ | ------------ |
| name        | `string` | The unique, human-readable name of the metric (e.g., 'Annual Revenue'). | `"ARR"`                              | Yes          |
| description | `string` | A detailed description of what the metric tracks.                       | `"Annual Recurring Revenue in USD."` | No           |
| data\_type  | `string` | The expected data type for the values of this metric.                   | `"NUMBER"`                           | Yes          |

Example Request Body:

```json
{
  "name": "Customer Tier",
  "description": "The service level tier the customer is subscribed to.",
  "data_type": "STRING"
}
```

***

### Responses&#x20;

#### Successful Response (201) (application/json)

Returns the newly created metric definition object.

| **Field**        | **Type**            | **Description**                                |
| ---------------- | ------------------- | ---------------------------------------------- |
| id               | `integer`           | The internal database ID of the definition.    |
| metric\_id       | `string`            | The unique external ID assigned to the metric. |
| name             | `string`            | The name of the metric.                        |
| description      | `string`            | The metric description.                        |
| data\_type       | `string`            | The expected data type.                        |
| organization\_id | `integer`           | The organization ID this metric belongs to.    |
| time\_created    | `string` (datetime) | Creation timestamp.                            |
| time\_updated    | `string` (datetime) | Last update timestamp.                         |

Example Response Body (201):

```json
{
  "id": 5,
  "metric_id": "METRIC-005",
  "name": "Customer Tier",
  "description": "The service level tier the customer is subscribed to.",
  "data_type": "STRING",
  "organization_id": 42,
  "time_created": "2025-11-27T10:06:09.459Z",
  "time_updated": "2025-11-27T10:06:09.459Z"
}
```

#### Validation Error (422) (application/json)

```json
{
  "detail": [
    {
      "loc": [
        "string",
        0
      ],
      "msg": "string",
      "type": "string"
    }
  ]
}
```


# List Metric Definitions

List all metric definitions that have been created for the organization.

GET /external/v1/external/v1/customers/metrics

### Request&#x20;

#### Parameters

* None

### Responses

#### Successful Response (200) (application/json)

Returns an array of metric definition objects.

| **Field**        | **Type**            | **Description**                                                                          |
| ---------------- | ------------------- | ---------------------------------------------------------------------------------------- |
| id               | `integer`           | The internal database ID of the definition.                                              |
| metric\_id       | `string`            | The unique external ID assigned to the metric.                                           |
| name             | `string`            | The human-readable name of the metric.                                                   |
| description      | `string`            | A detailed description of what the metric tracks.                                        |
| data\_type       | `string`            | The expected data type for the values of this metric (e.g., `STRING`, `NUMBER`, `DATE`). |
| organization\_id | `integer`           | The organization ID this metric belongs to.                                              |
| time\_created    | `string` (datetime) | Creation timestamp.                                                                      |
| time\_updated    | `string` (datetime) | Last update timestamp.                                                                   |

Example Response Body (200):

```json
[
  {
    "id": 1,
    "metric_id": "ARR",
    "name": "Annual Recurring Revenue",
    "description": "Total yearly contract value.",
    "data_type": "NUMBER",
    "organization_id": 42,
    "time_created": "2025-11-20T10:00:00.000Z",
    "time_updated": "2025-11-27T10:09:07.006Z"
  },
  {
    "id": 2,
    "metric_id": "TIER",
    "name": "Customer Tier",
    "description": "Service level tier.",
    "data_type": "STRING",
    "organization_id": 42,
    "time_created": "2025-11-22T10:00:00.000Z",
    "time_updated": "2025-11-22T10:00:00.000Z"
  }
]
```


# Engagement


# Whatsapp

Engage your customer using whatsapp messaging. this feature gives you ability to send plain or templated messages like OTP to your user's whatsapp phone number

{% content-ref url="/spaces/7cjqx8r9XLywjSwcc7AQ/pages/kn9Y7BpwX9pRBHO6THBs" %}
[Send Whatsapp Message](/api-reference/engagement/whatsapp/send-whatsapp-message)
{% endcontent-ref %}

{% content-ref url="/spaces/7cjqx8r9XLywjSwcc7AQ/pages/byRqj9IKE9pPA1HkF29u" %}
[Send template message](/api-reference/engagement/whatsapp/send-template-message)
{% endcontent-ref %}

{% content-ref url="/spaces/7cjqx8r9XLywjSwcc7AQ/pages/vrhkm9C0Whu1VB8gVPv6" %}
[Create whatsapp template](/api-reference/engagement/whatsapp/create-whatsapp-template)
{% endcontent-ref %}

{% content-ref url="/spaces/7cjqx8r9XLywjSwcc7AQ/pages/OeeVyJoQOxbWIUW28BfN" %}
[Get whatsapp templates](/api-reference/engagement/whatsapp/get-whatsapp-templates)
{% endcontent-ref %}

{% content-ref url="/spaces/7cjqx8r9XLywjSwcc7AQ/pages/UgpURHGcYCXOOXhiaSDy" %}
[Update whatsapp template](/api-reference/engagement/whatsapp/update-whatsapp-template)
{% endcontent-ref %}

{% content-ref url="/spaces/7cjqx8r9XLywjSwcc7AQ/pages/bu51k1nzDLvNciZeBEDV" %}
[Delete whatsapp template](/api-reference/engagement/whatsapp/delete-whatsapp-template)
{% endcontent-ref %}


# Send Whatsapp Message

Send standalone plain WhatsApp message

Send Whatsapp Message

<mark style="color:green;">`POST`</mark> /external/v1/whatsapp/send

Send standalone plain WhatsApp message

**Headers**

| Name         | Value              |
| ------------ | ------------------ |
| Content-Type | `application/json` |
| x-api-key    | `<your api key>`   |

**Body**

```json
{
  "to": "string",
  "message": "string"
}
```

**Response**

{% tabs %}
{% tab title="200" %}

```json
{
  "success": true,
  "status": "string",
  "billing_units_charged": 0,
  "wallet_charge_applied": true,
  "message": "string"
}
```

{% endtab %}

{% tab title="422" %}

```json
{
  "detail": [
    {
      "loc": [
        "string",
        0
      ],
      "msg": "string",
      "type": "string",
      "input": "string",
      "ctx": {}
    }
  ]
}
```

{% endtab %}
{% endtabs %}


# Create whatsapp template

Create a WhatsApp template

## Create a new user

<mark style="color:green;">`POST`</mark> /external/v1/whatsapp/templates

Create a WhatsApp template

**Headers**

| Name         | Value              |
| ------------ | ------------------ |
| Content-Type | `application/json` |
| x-api-key    | `<api key>`        |

**Body**

<pre><code><strong>SAMPLE
</strong>body: "Your OTP is {{1}}"
variables: { "1": "otp_code" }
</code></pre>

```json
{
  "template_name": "string",
  "body": "string",
  "language": "en",
  "category": "string",
  "variables": {
    "additionalProp1": "string",
    "additionalProp2": "string",
    "additionalProp3": "string"
  },
  "content_types": {
    "additionalProp1": {}
  }
}
```

**Response**

{% tabs %}
{% tab title="200" %}

```json
{
  "success": true,
  "template_sid": "string",
  "template_name": "string",
  "language": "string",
  "variables": {
    "additionalProp1": {}
  },
  "content_types": {
    "additionalProp1": {}
  },
  "status": {
    "additionalProp1": {}
  }
}
```

{% endtab %}

{% tab title="422" %}

```json
{
  "detail": [
    {
      "loc": [
        "string",
        0
      ],
      "msg": "string",
      "type": "string",
      "input": "string",
      "ctx": {}
    }
  ]
}
```

{% endtab %}
{% endtabs %}


# Send template message

Send standalone WhatsApp template/content-SID message (e.g., OTP).

## Send template message

<mark style="color:green;">`POST`</mark> `/`external/v1/whatsapp/send-template

Send standalone WhatsApp template/content-SID message (e.g., OTP).

**Headers**

| Name         | Value              |
| ------------ | ------------------ |
| Content-Type | `application/json` |
| x-api-key    | `<api key>`        |

**Body**

{% hint style="info" %}

```
Create template:
body: "Your OTP is {{1}}"
variables: { "1": "otp_code" }
 
Send template message:
template_variables: { "1": "123456" }
```

{% endhint %}

```json
{
  "to": "string",
  "template_id": "string",
  "template_variables": {
    "additionalProp1": {}
  },
  "fallback_message": "string"
}
```

**Response**

{% tabs %}
{% tab title="200" %}

```json
{
  "success": true,
  "status": "string",
  "message_id": "string",
  "billing_units_charged": 0,
  "wallet_charge_applied": true,
  "message": "string"
}
```

{% endtab %}

{% tab title="422" %}

```json
{
  "detail": [
    {
      "loc": [
        "string",
        0
      ],
      "msg": "string",
      "type": "string",
      "input": "string",
      "ctx": {}
    }
  ]
}
```

{% endtab %}
{% endtabs %}


# Get whatsapp templates

List WhatsApp templates for your organisation.

## Get whatsapp templates

<mark style="color:green;">`GET`</mark> /external/v1/whatsapp/templates

List WhatsApp templates for your organisation.

**Headers**

| Name         | Value              |
| ------------ | ------------------ |
| Content-Type | `application/json` |
| x-api-key    | `<api key>`        |

**Parameters**

| Name  | Type   | value        |
| ----- | ------ | ------------ |
| limit | number | 50 (100 max) |

**Response**

{% tabs %}
{% tab title="200" %}

```json
[
  {
    "template_sid": "string",
    "template_name": "string",
    "language": "string",
    "variables": {
      "additionalProp1": {}
    },
    "content_types": {
      "additionalProp1": {}
    },
    "status": {
      "additionalProp1": {}
    }
  }
]
```

{% endtab %}

{% tab title="422" %}

```json
{
  "detail": [
    {
      "loc": [
        "string",
        0
      ],
      "msg": "string",
      "type": "string",
      "input": "string",
      "ctx": {}
    }
  ]
}
```

{% endtab %}
{% endtabs %}


# Update whatsapp template

Update a WhatsApp template by SID.

## Update whatsapp template

<mark style="color:green;">`POST`</mark> /external/v1/whatsapp/templates/{template\_sid}

Update a WhatsApp template by SID.

**Headers**

| Name         | Value              |
| ------------ | ------------------ |
| Content-Type | `application/json` |
| x-api-key    | `<api key>`        |

**Body**

```json
{
  "template_name": "string",
  "body": "string",
  "language": "string",
  "category": "string",
  "variables": {
    "additionalProp1": "string",
    "additionalProp2": "string",
    "additionalProp3": "string"
  },
  "content_types": {
    "additionalProp1": {}
  }
}
```

**Response**

{% tabs %}
{% tab title="200" %}

```json
{
  "template_sid": "string",
  "template_name": "string",
  "language": "string",
  "variables": {
    "additionalProp1": {}
  },
  "content_types": {
    "additionalProp1": {}
  },
  "status": {
    "additionalProp1": {}
  }
}
```

{% endtab %}

{% tab title="422" %}

```json
{
  "detail": [
    {
      "loc": [
        "string",
        0
      ],
      "msg": "string",
      "type": "string",
      "input": "string",
      "ctx": {}
    }
  ]
}
```

{% endtab %}
{% endtabs %}


# Delete whatsapp template

Delete a WhatsApp template by SID.

## Delete whatsapp template

<mark style="color:green;">`POST`</mark> /external/v1/whatsapp/templates/{template\_sid}

Delete a WhatsApp template by SID.

**Headers**

| Name         | Value              |
| ------------ | ------------------ |
| Content-Type | `application/json` |
| x-api-key    | `<api key>`        |

**Response**

{% tabs %}
{% tab title="200" %}

```json
{
  "success": true,
  "template_sid": "string",
  "message": "string"
}
```

{% endtab %}

{% tab title="422" %}

```json
{
  "detail": [
    {
      "loc": [
        "string",
        0
      ],
      "msg": "string",
      "type": "string",
      "input": "string",
      "ctx": {}
    }
  ]
}
```

{% endtab %}
{% endtabs %}


# SMS


# Sender ID

## Request for a sender ID

<mark style="color:green;">`POST`</mark> <https://api.cuoral.com/external/v1/external/v1/sms/sender-id/request>

Request for a new sender ID

**Headers**

| Name         | Value              |
| ------------ | ------------------ |
| Content-Type | `application/json` |
| x-api-key    | `<api_key>`        |

**Body**

| Name        | Type   | Description                |
| ----------- | ------ | -------------------------- |
| `sender_id` | string | Sender ID name e.g Cuoral  |
| `use_case`  | string | trnasactional or marketing |

**Response**

{% tabs %}
{% tab title="200" %}

```json
{
 "status":true,
 "message":"Sender ID request sent"
}
```

{% endtab %}

{% tab title="400" %}

```json
{
  "error": "Invalid request"
}
```

{% endtab %}
{% endtabs %}


# Send SMS

Send an sms to any number

## Send a new message

<mark style="color:green;">`POST`</mark> [https://api.cuoral.com/external/v1/external/v1/sms/send](https://api.cuoral.com/external/v1/external/v1/sms/sender-id/request)

The **Cuoral Send Single SMS API** enables you to send a one-time SMS message to a specific recipient with ease. This lightweight and reliable endpoint is ideal for sending OTPs, personal notifications, order updates, or any time-sensitive information. With fast delivery, support for custom sender IDs, and simple integration, Cuoral makes it easy to add SMS functionality to your applications or workflows.

**Headers**

| Name         | Value              |
| ------------ | ------------------ |
| Content-Type | `application/json` |
| x-api-key    | `<api_key>`        |

**Body**

| Name        | Type   | Description               |
| ----------- | ------ | ------------------------- |
| `sender_id` | string | Sender ID name e.g Cuoral |
| `channel`   | string | generic or dnd            |
| to          | string | phone number              |
| message     | string | your OTP is 111111        |

**Response**

{% tabs %}
{% tab title="200" %}

```json
{
 "status":true,
 "message":"Message sent successfully"
}
```

{% endtab %}

{% tab title="400" %}

```json
{
  "error": "Invalid request"
}
```

{% endtab %}
{% endtabs %}


# Send Bulk SMS

Send bulk sms to as many phone number you want at a time

## Send a new bulk sms

<mark style="color:green;">`POST`</mark> <https://api.cuoral.com/external/v1/external/v1/sms/send/bulk>

The **Cuoral Send Bulk SMS API** allows you to deliver SMS messages to multiple recipients in a single request. Designed for scalability and speed, this endpoint is perfect for sending mass notifications, marketing messages, alerts, or transactional updates. Whether you're reaching hundreds or thousands of users, Cuoral ensures fast, reliable, and secure SMS delivery. Ideal for businesses and developers looking to integrate powerful messaging capabilities directly into their applications.

**Headers**

| Name         | Value              |
| ------------ | ------------------ |
| Content-Type | `application/json` |
| x-api-key    | `<api_key>`        |

**Body**

| Name        | Type   | Description               |
| ----------- | ------ | ------------------------- |
| `sender_id` | string | Sender ID name e.g Cuoral |
| `channel`   | string | generic or dnd            |
| to          | array  | array of phone numbers    |
| message     | string | your OTP is 111111        |

**Response**

{% tabs %}
{% tab title="200" %}

```json
{
 "status":true,
 "message":"Message sent successfully"
}
```

{% endtab %}

{% tab title="400" %}

```json
{
  "error": "Invalid request"
}
```

{% endtab %}
{% endtabs %}


# Email


# Send Single email

## Send email to users

<mark style="color:green;">`POST`</mark> [/external/v1/transactional-email/send](https://api.cuoral.com/xxxxdocs#/Transactional%20Email%20\(External\)/send_transactional_email_external_v1_transactional_email_send_post)<br>

Send Transactional Email\
\
Use this endpoint to send OTPs, password resets, welcome emails, order confirmations, notifications, and other transactional messages from your application.

**Headers**

| Name                | Value              |
| ------------------- | ------------------ |
| Content-Type        | `application/json` |
| X-Transactional-Key | cte\_xxxxxxxxxxxx  |

**Body**

| Name          | Type   | Description                 |
| ------------- | ------ | --------------------------- |
| to\_email     | string | email                       |
| subject       | string | email subject               |
| html\_content | string |                             |
| email\_type   | string | e.g otp                     |
| merge\_data   | string | e.g {"otp\_code": "482910"} |

**Response**

{% tabs %}
{% tab title="200" %}

```json
{
  "to_email": "user@example.com",
  "subject": "string",
  "html_content": "string",
  "text_content": "string",
  "template_uid": "string",
  "to_name": "string",
  "cc": [
    "user@example.com"
  ],
  "bcc": [
    "user@example.com"
  ],
  "from_email": "user@example.com",
  "from_name": "string",
  "reply_to": "user@example.com",
  "email_type": "custom",
  "category": "string",
  "merge_data": {
    "additionalProp1": "string",
    "additionalProp2": "string",
    "additionalProp3": "string"
  },
  "extra_metadata": {
    "additionalProp1": {}
  }
}
```

{% endtab %}

{% tab title="400" %}

```json
{
  "error": "Invalid request"
}
```

{% endtab %}
{% endtabs %}


# Send Bulk Email

## Send bulk email

<mark style="color:green;">`POST`</mark> [/external/v1/transactional-email/send/bulk](https://api.cuoral.com/xxxxdocs#/Transactional%20Email%20\(External\)/send_bulk_transactional_email_external_v1_transactional_email_send_bulk_post)

[<br>](https://api.cuoral.com/xxxxdocs#/Transactional%20Email%20\(External\)/send_bulk_transactional_email_external_v1_transactional_email_send_bulk_post)Send Bulk Transactional Email<br>

**Headers**

| Name                | Value              |
| ------------------- | ------------------ |
| Content-Type        | `application/json` |
| X-Transactional-Key | cte\_xxxxxxxxxxxx  |

**Body**

```json
{
  "emails": [
    {
      "to_email": "user@example.com",
      "subject": "string",
      "html_content": "string",
      "text_content": "string",
      "template_uid": "string",
      "to_name": "string",
      "cc": [
        "user@example.com"
      ],
      "bcc": [
        "user@example.com"
      ],
      "from_email": "user@example.com",
      "from_name": "string",
      "reply_to": "user@example.com",
      "email_type": "custom",
      "category": "string",
      "merge_data": {
        "additionalProp1": "string",
        "additionalProp2": "string",
        "additionalProp3": "string"
      },
      "extra_metadata": {
        "additionalProp1": {}
      }
    }
  ]
}
```

**Response**

{% tabs %}
{% tab title="200" %}

```json
{
  "total": 0,
  "successful": 0,
  "failed": 0,
  "results": [
    {
      "to_email": "string",
      "success": true,
      "log_uid": "string",
      "message_id": "string",
      "error": "string"
    }
  ]
}
```

{% endtab %}

{% tab title="400" %}

```json
{
  "error": "Invalid request"
}
```

{% endtab %}
{% endtabs %}


# Churn Intelligence


# Events


# Send custom events

Log and published custom events to the real-time processing pipeline for immediate analysis (health scoring, anomaly detection, abandonment detection).

## Send custom events

<mark style="color:green;">`POST`</mark> /external/v1/custom-events/log<br>

Log and published custom events to the real-time processing pipeline for immediate analysis (health scoring, anomaly detection, abandonment detection).

**Customer resolution**: Each event should include at least one of:

* `session_id` - Cuoral session ID
* `customer_id` - Your customer ID as stored in Cuoral
* `customer_email` - Customer email address<br>

**Headers**

| Name         | Value              |
| ------------ | ------------------ |
| Content-Type | `application/json` |
| x-api-key    | `<api_key>`        |

**Body**

```json
{
  "events": [
    {
      "name": "string",
      "category": "string",
      "properties": {
        "custom_props1":"",
        "custom_props2":""
      },
      "event_timestamp": "2026-02-26T14:44:17.435Z",
      "url": "string",
      "session_id": "string",
      "customer_id": "string",
      "customer_email": "string"
    },
  ]
}
```

**Response**

{% tabs %}
{% tab title="200" %}

```json
{
  "status": "success",
  "saved": 0,
  "skipped": 0,
  "errors": [
    "string"
  ]
```

{% endtab %}

{% tab title="422" %}

```json
{
  "detail": [
    {
      "loc": [
        "string",
        0
      ],
      "msg": "string",
      "type": "string",
      "input": "string",
      "ctx": {}
    }
  ]
}
```

{% endtab %}
{% endtabs %}


# Authentication

Cuoral uses **HMAC-SHA256 signatures** to authenticate webhook requests and ensure that webhook events are coming from Cuoral.

Every webhook request includes a signature in the `X-Cuoral-Webhook-Signature` header.

Your webhook endpoint should verify this signature before processing the request.

### Signature Header

The signature is provided in the following request header:

```http
X-Cuoral-Webhook-Signature: <signature>
```

The signature is generated using:

* **Algorithm:** HMAC-SHA256
* **Secret:** Your organization's `api_key`
* **Payload:** The webhook request payload serialized as JSON with sorted keys

### How Verification Works

When Cuoral sends a webhook, it:

1. Serializes the webhook payload as JSON with keys sorted alphabetically.
2. Uses your organization's `api_key` as the HMAC secret.
3. Generates an HMAC-SHA256 hash.
4. Sends the resulting hexadecimal signature in the `X-Cuoral-Webhook-Signature` header.

Your application should perform the same calculation and compare the generated signature with the value provided in the request header.

If the signatures match, the webhook can be considered authentic.

### Node.js

```javascript
const crypto = require('crypto');

function verifyWebhookSignature(payload, signatureHeader, publicKey) {
  const payloadString = JSON.stringify(
    payload,
    Object.keys(payload).sort()
  );

  const expectedSignature = crypto
    .createHmac('sha256', publicKey)
    .update(payloadString)
    .digest('hex');

  return crypto.timingSafeEqual(
    Buffer.from(expectedSignature),
    Buffer.from(signatureHeader)
  );
}
```

### Python

```python
import hmac
import hashlib
import json

def verify_webhook_signature(
    payload: dict,
    signature_header: str,
    public_key: str
) -> bool:
    payload_str = json.dumps(payload, sort_keys=True)

    expected_signature = hmac.new(
        public_key.encode("utf-8"),
        payload_str.encode("utf-8"),
        hashlib.sha256
    ).hexdigest()

    return hmac.compare_digest(
        expected_signature,
        signature_header
    )
```

### Important: Verify the Raw Payload

For maximum reliability, webhook signature verification should be performed against the **raw request body** before your framework parses or transforms the JSON.

Re-serializing JSON can produce a different string due to differences in whitespace, escaping, or key ordering.

If your framework provides access to the raw request body, use it when calculating the HMAC.

### Security Recommendations

* Always verify the `X-Cuoral-Webhook-Signature` header before processing a webhook.
* Keep your organization's api\_key secret.
* Use HTTPS for your webhook endpoint.
* Use a constant-time comparison when comparing signatures.
* Return a successful HTTP status only after the webhook has been accepted and validated.


# Message Event

The `message` event is triggered when a new message is created in a conversation.

Cuoral sends this event to the organization's registered webhook `target_url`.

### Event Type

The `X-Event` header identifies the type of webhook event.

For message events:

```http
X-Event: message
```

### Request Headers

A message webhook includes the following headers:

| Header                       | Description                                                     |
| ---------------------------- | --------------------------------------------------------------- |
| `Content-Type`               | The content type of the request. Always `application/json`.     |
| `X-Event`                    | The webhook event type. For this event, the value is `message`. |
| `X-Cuoral-Webhook-Signature` | HMAC-SHA256 signature used to authenticate the webhook request. |

For information about verifying webhook signatures, see the **Webhook Authentication** page.

### Payload

The message event payload contains information about the customer and the message.

```json
{
  "email": "customer@example.com",
  "name": "John Doe",
  "message": "Hello, I need help with my account.",
  "sender": "customer"
}
```

### Payload Fields

| Field     | Type     | Description                                                         |
| --------- | -------- | ------------------------------------------------------------------- |
| `email`   | `string` | The email address of the customer associated with the conversation. |
| `name`    | `string` | The name of the customer associated with the conversation.          |
| `message` | `string` | The text content of the message.                                    |
| `sender`  | `string` | Identifies who sent the message.                                    |

#### `sender`

The `sender` field indicates who created the message.

| Value      | Description                                               |
| ---------- | --------------------------------------------------------- |
| `customer` | The message was sent by the customer.                     |
| `internal` | The message was sent by an internal agent, admin, or bot. |

### Example Request

```http
POST /webhooks/cuoral HTTP/1.1
Content-Type: application/json
X-Event: message
X-Cuoral-Webhook-Signature: <signature>

{
  "email": "customer@example.com",
  "name": "John Doe",
  "message": "Hello, I need help with my account.",
  "sender": "customer"
}
```

### Response

Your webhook endpoint should return one of the following status codes to acknowledge successful receipt:

* `200 OK`
* `201 Created`
* `204 No Content`

For example:

```http
HTTP/1.1 200 OK
```

### Retries

If your endpoint responds with any status code other than `200`, `201`, or `204`, Cuoral will retry the webhook delivery.

Cuoral will attempt to deliver the webhook up to **3 additional times** using an exponential backoff strategy.

Your webhook handler should therefore be **idempotent** and safely handle the same event being received more than once.


