CRM Endpoints
Connect WAzion with your external CRM or ERP so that the AI and the sidebar can access customer, order, and product data from your system.
What are CRM Endpoints?
CRM Endpoints allow WAzion to query information from your external system (CRM, ERP, own database). Unlike AI Functions (which the AI decides when to call), these endpoints are called automatically when the agent opens a conversation or searches for information.
For the Side Panel
Shows customer data alongside the conversation (orders, history, contact information).
For AI
Provide initial context about the client so the AI responds with relevant information.
1 sidePanel_CustomerInfo
Provides customer information displayed in the extension’s sidebar when the agent opens a conversation.
Sent parameters
| phone | Customer’s phone number (E.164) |
| token | Authentication token |
| relatedphones | Related phones (array) |
Expected answer
{
"found": true,
"customers": [
{
"name": "Juan García",
"email": "juan@email.com",
"phone": "+34612345678",
"shopUrl": "https://tucrm.com/c/123",
"ordersCount": 5,
"totalSpent": 450.00,
"totalRefunded": 0,
"state": "VIP - Activo",
"orders": [...],
"abandonedCarts": [...]
}
]
}
Note: You can use “customer“ (object) or “customers“ (array) to support multiple clients. ALWAYS returns HTTP 200, even if you don’t find a client (use “found“: false).
📦 See complete response structure (orders, items, tracking, abandonedCarts)
Complete JSON structure with all supported fields:
{
"found": true,
"customers": [
{
"// Datos básicos del cliente"
"name": "Juan García López",
"email": "juan@email.com",
"phone": "+34612345678",
"shopUrl": "https://tu-crm.com/clientes/123",
"ordersCount": 15,
"totalSpent": 1250.50,
"totalRefunded": 50.00,
"totalDiscounts": 25.00,
"state": "Cliente VIP",
"// Array de pedidos"
"orders": [
{
"name": "#ORD-2025-001",
"date": "2025-01-10T14:30:00Z",
"amount": 129.99,
"netAmount": 104.99,
"totalRefunded": 25.00,
"totalDiscounts": 10.00,
"currency": "EUR",
"cancelled": false,
"shopUrl": "https://tu-crm.com/pedidos/1234",
"note": "Entregar por la tarde",
"// Items del pedido"
"items": [
{
"title": "Camiseta Premium Azul - Talla L",
"quantity": 2,
"currentQuantity": 2,
"amount": 59.98,
"originalAmount": 59.98,
"discountedAmount": 59.98,
"discount": 0,
"refundedQuantity": 0,
"refundedAmount": 0,
"status": "active" // active | partially_refunded | fully_refunded
}
],
"// Tracking de envíos"
"tracking": [
{
"number": "ES123456789012",
"company": "SEUR",
"url": "https://www.seur.com/tracking/...",
"displayStatus": "Entregado"
}
]
}
],
"// Carritos abandonados"
"abandonedCarts": [
{
"date": "2025-01-14T18:45:00Z",
"url": "https://tu-tienda.com/cart/recover/abc123",
"items": [
{
"title": "Zapatillas Running Pro",
"quantity": 1,
"amount": 129.99
}
]
}
]
}
]
}
Customer fields:
| Field | Type | Required |
|---|---|---|
| name | string | Yes |
| string | Yes | |
| phone | string | Yes |
| shopUrl | string | No |
| ordersCount | number | Yes |
| totalSpent | number | Yes |
| totalRefunded | number | No |
| totalDiscounts | number | No |
| state | string | Yes |
| orders | array | No |
| abandonedCarts | array | No |
Order fields:
| Field | Type | Description |
|---|---|---|
| name | string | Order ID/Number |
| date | string | ISO 8601 Date |
| amount | number | Order total |
| netAmount | number | Net amount (after refunds) |
| currency | string | Currency code (EUR, USD...) |
| cancelled | boolean | true if it is canceled |
| shopUrl | string | URL to the order in your system |
| note | string | Order note |
| items | array | Order products |
| tracking | array | Shipments/tracking |
Item fields:
| Field | Type | Description |
|---|---|---|
| title | string | Product name |
| quantity | number | Quantity ordered |
| currentQuantity | number | Current amount (after refunds) |
| amount | number | Total price of the item |
| refundedQuantity | number | Reimbursed units |
| refundedAmount | number | Refunded amount |
| status | string | active | partially_refunded | fully_refunded |
Tracking fields:
| Field | Type | Description |
|---|---|---|
| number | string | Tracking number |
| company | string | Shipping company |
| url | string | Tracking URL |
| displayStatus | string | Readable status (Delivered, In transit...) |
Translation labels (UI)
For the extension to display the texts in the correct language, include the “labels“ object with the translations:
{
"found": true,
"customer": { ... },
"labels": {
"label_languages": "es",
"customer_info": "Información del Cliente",
"name": "Nombre",
"email": "Email",
...
}
}
See complete table of available labels
| Key | Default (ES) | Description |
|---|---|---|
| label_languages | "es" | Tag language. If it differs from the store language, WAzion translates them automatically |
| customer_info | "Información del Cliente" | Customer Section Title |
| name | "Nombre" | Name tag |
| "Email" | Email label | |
| phone | "Teléfono" | Phone label |
| orders_count | "Pedidos" | Order counter |
| total_spent | "Total gastado" | Total spent |
| total_refunded | "Total reembolsado" | Refunds |
| discounts | "Descuentos" | Discounts |
| customer_state | "Estado" | Customer status |
| recent_orders | "Pedidos recientes" | Order Title |
| order | "Pedido" | Individual order |
| orders | "Pedidos" | Orders |
| order_number | "Nº de pedido" | Order number |
| date | "Fecha" | Date |
| amount | "Importe" | Amount |
| net_amount | "Importe neto" | Net amount |
| subtotal | "Subtotal" | Subtotal |
| discount | "Descuento" | Individual discount |
| refund | "Reembolso" | Refund |
| refunded | "Reembolsado" | Refunded badge |
| cancelled | "Cancelado" | Cancelled badge |
| cancel_reason | "Motivo cancelación" | Reason for cancellation |
| items | "Artículos" | Products/Items |
| item_status | "Estado artículo" | Item condition |
| quantity | "Cantidad" | Amount |
| original_quantity | "Cantidad original" | Original amount |
| current_quantity | "Cantidad actual" | Current quantity |
| sku | "SKU" | SKU |
| shipments | "Envíos" | Shipments |
| shipping | "Envío" | Individual shipment |
| tracking_number | "Nº seguimiento" | Tracking number |
| no_shipping_info | "Sin información de envío" | No shipping |
| order_note | "Nota del pedido" | Order notes |
| fully_refunded | "Reembolsado completamente" | Item 100% refunded |
| partially_refunded | "Reembolsado parcialmente" | Item partially refunded |
| active | "Activo" | Active status |
| abandoned_carts | "Carrito/s abandonado/s" | Abandoned carts |
| copy_link | "Copiar enlace" | Copy button |
| view_customer | "Ver perfil del cliente" | View Customer Button |
| view_profile | "Ver perfil" | View profile button |
| remove_assignment | "Quitar asignación" | Unlink button |
| not_found | "Cliente no encontrado" | Message not found |
| no_orders | "Sin pedidos" | Without orders |
| error | "Error" | Error prefix |
| Customer status | ||
| state_no_orders | "Sin pedidos" | Status: no orders |
| state_conflictive | "Conflictivo" | Status: conflicted |
| state_processing | "Procesando" | Status: processing |
| state_new_orders | "Pedidos nuevos" | Status: new orders |
| state_recent_orders | "Pedidos recientes" | Status: recent orders |
| state_old_orders | "Pedidos antiguos" | Status: old orders |
Automatic translation: If you send labels in one language (e.g., English with label_languages: “en“) and the store is set to another (e.g., Spanish), WAzion will automatically translate all the labels. This allows you to keep your CRM in a single language.
2 ai_CustomerInitialInfo
Provide initial context about the client to the AI. This text is included in the AI prompt so that it can respond with relevant information about the client.
Sent parameters
| phone | Customer’s phone number (E.164) |
| token | Authentication token |
| relatedphones | Related phones (array) |
| order | Mentioned order number (optional) |
| Customer email (optional) | |
| test | true“ if it is a test |
Expected answer
{
"found": true,
"info": "Cliente: Juan García.
VIP: Sí. Total pedidos: 5.
Último pedido: #12345 (15/01/2024)
Estado: Entregado.
Productos frecuentes: Zapatos, Camisetas.
ALERTA: Cliente alérgico al gluten."
}
Important: The “info“ field is free text that the AI will read as context. It includes purchase history, preferences, important alerts (allergies, previous issues), customer status, preferred payment/shipping method, etc.
3 sidePanel_CustomerFindToJoin
Allows you to search for customers in your system to link them to a WhatsApp conversation. Useful when the customer writes from a different number than the one registered.
Sent parameters
| query | Search text |
| token | Authentication token |
| phone | Current phone number of the conversation |
| relatedphones | Phones already linked (array, to exclude them) |
Expected answer
{
"customers": [
{
"id": "cust_123",
"name": "Juan García",
"email": "juan@email.com",
"phone": "+34612345678"
},
{
"id": "cust_456",
"name": "Juan Martínez",
"email": "juanm@email.com",
"phone": "+34687654321"
}
]
}
Note: You can use “customers“ or “results“ as the name of the array. The “phone“ field of each customer allows WAzion to automatically link the conversation.
4 search_Products
Allows searching for products in your external catalog. Results are displayed in the side panel and the agent can share them with the customer. The AI can also search for products to answer questions.
Note for CRM Custom: WAzion acts as a transparent proxy for this endpoint. Your CRM response is returned directly to the extension without modification. You must implement the full documented structure if you want to take advantage of all functionalities. Enriched fields (target_locale, title_copy, raw, etc.) are only automatically generated for Shopify stores.
Sent parameters
| Parameter | Type | Description |
|---|---|---|
| q | string | Search text (minimum 2 characters) |
| token | string | Store authentication token |
| phone | string | Customer phone number (E.164) - for country-specific URLs |
| format | string | ai“ when AI searches for products (response structure changes) |
| target_locale | string | Language detected from the client based on their phone (e.g., “es“, “de“, “fr“) |
| test | boolean | true if it is a connection test |
New: target_locale - WAzion automatically detects the client’s language based on their phone prefix. For example, +34... → “es“, +49... → “de“. Use this parameter to return products in the correct language.
Response NORMAL format (side panel)
When the agent searches for products from the panel. Includes fields to copy/share.
{
"http_status": 200,
"count": 1,
"products": [
{
"title": "Camiseta Premium",
"title_copy": "Premium T-Shirt",
"handle": "camiseta-premium",
"description": "Descripción truncada a 500 chars...",
"sku": "CAM-001",
"image": "https://proxy/imagen.jpg",
"url": "https://tutienda.es/products/camiseta-premium",
"variants": [
{
"id": "12345678",
"title": "M / Rojo",
"title_copy": "M / Red",
"has_custom_title": true,
"sku": "CAM-001-M-RED",
"image": "https://proxy/variante.jpg",
"url": "https://tutienda.es/products/camiseta-premium?variant=12345678"
}
]
}
],
"target_locale": "en",
"raw": { ... }
}
AI format response (when format=“ai“)
When AI searches for products. Simplified structure without copy fields, complete description.
{
"count": 1,
"products": [
{
"title": "Premium T-Shirt",
"handle": "camiseta-premium",
"description": "Descripción COMPLETA sin truncar para contexto IA...",
"sku": "CAM-001",
"url": "https://tutienda.es/products/camiseta-premium",
"variants": [
{
"id": "12345678",
"title": "M / Red",
"sku": "CAM-001-M-RED",
"url": "https://tutienda.es/products/camiseta-premium?variant=12345678"
}
]
}
],
"locale": "en"
}
View all product fields
| Field | Normal | AI | Description |
|---|---|---|---|
| title | Original | Translated | Product title |
| title_copy | Translated | - | Title to copy (client’s language) |
| handle | ✓ | ✓ | Product slug |
| description | 500 chars | Complete | Description (cleaned HTML) |
| sku | ✓ | ✓ | SKU of the first variant |
| image | ✓ | - | Image URL (may be proxied) |
| url | ✓ | ✓ | Localized product URL |
| variants | ✓ | ✓ | Array of variants |
See fields of each variant
| Field | Normal | AI | Description |
|---|---|---|---|
| id | ✓ | ✓ | Variant ID |
| title | Original | Translated | Variant title (e.g., “M / Red“) |
| title_copy | Translated | - | Title to copy (client’s language) |
| has_custom_title | ✓ | - | true if it has a custom title (not “Default Title“) |
| sku | ✓ | ✓ | Variant SKU |
| image | ✓ | - | Specific variant image (or of the product if it does not have one) |
| url | ✓ | ✓ | URL with ?variant=ID |
View root response fields
| Field | Normal | AI | Description |
|---|---|---|---|
| http_status | ✓ | - | HTTP response code |
| count | ✓ | ✓ | Number of products found |
| products | ✓ | ✓ | Product array |
| target_locale | ✓ | - | Locale used for translations |
| locale | - | ✓ | Locale used (AI format only) |
| raw | ✓ | - | Raw response from the source (debug) |
🌍 Country-specific URLs
The parameter phone contains the customer’s phone number (E.164). Use it to detect their country and return tailored URLs:
- Country-specific domains (tutienda.es, tutienda.de, tutienda.fr)
- Routes with language prefix (/es/, /de/, /fr/)
- Locale parameters (?locale=es_ES)
- Specific Shopify Markets
Example: If the phone number starts with +34 (Spain), return URLs with the domain .es or /es/.
5 globalSearch
Global customer search for the floating searcher of the extension. Allows searching by name, email, phone, or order number.
Sent parameters
| Parameter | Type | Description |
|---|---|---|
| query | string | Search text (minimum 2 characters) |
| token | string | Store authentication token |
Expected answer
{
"found": true,
"customers": [
{
"id": "cust_123",
"name": "Juan García López",
"email": "juan.garcia@email.com",
"phone": "+34612345678",
"customerUrl": "https://tucrm.com/clientes/123",
"orders": [
{
"number": "#ORD-1234",
"url": "https://tucrm.com/pedidos/1234"
},
{
"number": "#ORD-1233",
"url": "https://tucrm.com/pedidos/1233"
}
]
}
]
}
Fields of each client
| Field | Type | Required | Description |
|---|---|---|---|
| id | string | Yes | Unique client ID in your system |
| name | string | Yes | Client’s full name |
| string | Yes | Client’s email | |
| phone | string | Yes | Client’s phone number (E.164 preferred, e.g.: +34612345678) |
| customerUrl | string | No | URL to view the client in your CRM (displayed as a clickable link) |
| orders | array | No | Array with the latest orders (maximum 3) |
Fields of each order (orders[])
| Field | Type | Required | Description |
|---|---|---|---|
| number | string | Yes | Order number (e.g., “#ORD-1234“) |
| url | string | No | URL to view the order in your system |
Important notes
- • The
phonefield is mandatory for the agent to be able to open the conversation with that client. - • A maximum of 15 results will be shown to the user (automatically limited).
- • Phone duplicates are automatically deleted.
- • If
customerUrlis present, the customer’s name will be a clickable link. - • Orders are displayed as individual links below the customer.
Technical Specifications
Timeouts
| globalSearch | 5s |
| Other endpoints | 10s |
| Connect timeout | 5s |
| Retries | No |
HTTP Methods
| Configurable | GET / POST |
| Body (POST) | JSON / Form |
| Parameters (GET) | Query string |
Requirements
- HTTPS mandatory
- Valid JSON response
- HTTP 200 always (use found:false if there is no data)
Difference with AI Functions
CRM Endpoints do NOT automatically retry if they fail. They are called in real time while the agent is working, so they must respond quickly. If they fail, the information simply does not appear.
Types of Authentication
Example URL (GET + query auth)
// WAzion llamará:
https://tu-api.com/customer
?phone=+34612345678
&token=tu-token-secreto
&relatedphones=["+34698765432"]
&test=false
Implementation Examples
<?php
// Endpoint: /api/customer-info
header('Content-Type: application/json');
// Obtener parámetros (soporta GET y POST)
$input = json_decode(file_get_contents('php://input'), true) ?? $_GET;
$phone = $input['phone'] ?? '';
$token = $input['token'] ?? '';
$relatedPhones = $input['relatedphones'] ?? [];
$order = $input['order'] ?? '';
$isTest = ($input['test'] ?? '') === 'true';
// Verificar token
if ($token !== 'tu-token-secreto') {
echo json_encode(['found' => false, 'error' => 'No autorizado']);
exit;
}
// Si es test, devolver datos de ejemplo
if ($isTest) {
echo json_encode([
'found' => true,
'customers' => [[
'name' => 'Cliente de Prueba',
'email' => 'test@example.com',
'phone' => '+34612345678',
'shopUrl' => 'https://tucrm.com/cliente/123',
'ordersCount' => 3,
'totalSpent' => 150.00,
'state' => 'Activo',
'orders' => []
]]
]);
exit;
}
// Buscar por teléfono principal + relacionados
$allPhones = array_merge([$phone], $relatedPhones);
$customers = buscarClientesPorTelefonos($allPhones);
echo json_encode([
'found' => !empty($customers),
'customers' => $customers
]);
const express = require('express');
const app = express();
app.use(express.json());
const SECRET_TOKEN = 'tu-token-secreto';
app.all('/api/customer-info', async (req, res) => {
// Soporta GET y POST
const params = { ...req.query, ...req.body };
const { phone, token, relatedphones = [], order, test } = params;
// Verificar token
if (token !== SECRET_TOKEN) {
return res.json({ found: false, error: 'No autorizado' });
}
// Datos de test
if (test === 'true') {
return res.json({
found: true,
customers: [{
name: 'Cliente de Prueba',
email: 'test@example.com',
phone: '+34612345678',
shopUrl: 'https://tucrm.com/cliente/123',
ordersCount: 3,
totalSpent: 150.00,
state: 'Activo',
orders: []
}]
});
}
// Buscar clientes
const allPhones = [phone, ...relatedphones];
const customers = await buscarClientesPorTelefonos(allPhones);
res.json({ found: customers.length > 0, customers });
});
app.listen(3000);
from flask import Flask, request, jsonify
app = Flask(__name__)
SECRET_TOKEN = 'tu-token-secreto'
@app.route('/api/customer-info', methods=['GET', 'POST'])
def customer_info():
# Soporta GET y POST
params = {**request.args, **request.get_json(silent=True) or {}}
phone = params.get('phone', '')
token = params.get('token', '')
related_phones = params.get('relatedphones', [])
is_test = params.get('test') == 'true'
# Verificar token
if token != SECRET_TOKEN:
return jsonify({'found': False, 'error': 'No autorizado'})
# Datos de test
if is_test:
return jsonify({
'found': True,
'customers': [{
'name': 'Cliente de Prueba',
'email': 'test@example.com',
'phone': '+34612345678',
'shopUrl': 'https://tucrm.com/cliente/123',
'ordersCount': 3,
'totalSpent': 150.00,
'state': 'Activo',
'orders': []
}]
})
# Buscar clientes
all_phones = [phone] + related_phones
customers = buscar_clientes_por_telefonos(all_phones)
return jsonify({'found': len(customers) > 0, 'customers': customers})
Change log
sidePanel_CustomerInfo now automatically translates all labels if label_languages differs from the store language. You can keep your CRM in a single language (e.g., English) and WAzion takes care of the translations.
WAzion now sends the client’s detected language (based on their phone prefix) as the parameter target_locale. Useful for returning products in the customer’s correct language.