Skip to main content

Introduction

This section provides the official documentation for using Lovi’s API with WhatsApp via Postman. It includes detailed instructions on how to set up and test API requests for WhatsApp integration, ensuring smooth communication through the platform. Authentication is performed using tokens that enable basic authentication for the API services. For more details on how to authenticate, please refer to the Authentication page. The Lovi API supports WhatsApp notifications with multimedia content, dynamic placeholders, scheduled delivery, and conversation flow integration.

Key Features:

  • WhatsApp notifications with multimedia support
  • Dynamic content personalization with placeholders
  • Scheduled message delivery with timezone support
  • Conversation flow integration
  • Two data structure formats (nested and flat)

📣 Send WhatsApp Notification

To send a notification via the Lovi API, make a POST request to the endpoint with the necessary parameters and authentication.
Method: POST Format: JSON

Endpoint

POST https://cloud.lovi.ai/functions/v1/notify?access_key={YOUR_ACCESS_KEY}

Query Parameters

ParameterRequiredDescription
access_keyYesYour unique API access key.
unflattenNoIf set to true, the body must contain flat variables (no nested objects). If set to false or omitted, the body can contain nested objects.
Example URLs:
POST https://cloud.lovi.ai/functions/v1/notify?access_key=your-api-key
POST https://cloud.lovi.ai/functions/v1/notify?access_key=your-api-key&unflatten=true

Headers

KeyValueRequiredDescription
Content-Typeapplication/jsonYesIndicates that the request body is in JSON format.
Note: Authentication is handled via the access_key parameter in the URL, not through headers.

📋 Request Parameters

The API supports two data structure formats controlled by the unflatten parameter.

Required Parameters

ParameterTypeDescriptionExample
contact OR contactsObject OR ArrayChoose one: Single contact (object) OR Multiple contacts (array, max 100)See below
language_templateStringLanguage code for the template"es_ES", "en_US"
name_templateStringName of the approved template"welcome_user"
recipient_idStringID or phone number of the recipient"34666033135"
notification_typeStringType for analytics"marketing", "transactional", "utility"
campaign_nameStringCampaign identifier name"Black Friday 2024"
Important: You must use either contact (for single recipient) OR contacts (for multiple recipients), but NOT both.

Optional Parameters

ParameterTypeDescriptionWhen to UseExample
contact.nameStringContact’s name for personalizationFor personalized messages"Juan Pérez"
contact.emailStringContact’s emailFor additional contact data"[email protected]"
name_eventStringEvent to trigger conversation flowsWhen you want to start a specific conversation flow"intent-general-push"
datetime_sendingDateTimeScheduled delivery time (ISO 8601)When you want to schedule the message for later"2024-12-25T10:30:00"
timezoneStringTimezone for scheduled deliveryWhen using datetime_sending"Europe/Madrid"
components_push.*MixedTemplate components (text, media)When template requires dynamic contentSee components section

👥 Single vs Multiple Recipients

Using contact - Send to One Person

Use contact when you want to send a notification to one recipient. Structure:
{
  "contact": {
    "number": "34666033135",
    "name": "John",
    "email": "[email protected]"
  }
}
  • contact is an object (not a list)
  • Required field: number
  • Optional fields: name, email, and any custom fields

Using contacts - Send to Multiple People (Bulk Sending)

Use contacts when you want to send the same notification to multiple recipients at once. Structure:
{
  "contacts": [
    {
      "number": "34666033135",
      "name": "John"
    },
    {
      "number": "34777044246",
      "name": "Sarah"
    },
    {
      "number": "34888055357",
      "name": "Michael"
    }
  ]
}
  • contacts is a list/array (not a single object)
  • Maximum: 100 contacts per request
  • Each contact in the list must have a number
  • Optional fields: name, email, and any custom fields
Important Restrictions:
  • ⚠️ Cannot use unflatten=true with contacts - Bulk sending only works with nested structure
  • ⚠️ Cannot use both contact and contacts in the same request - choose one
  • ⚠️ contacts list cannot be empty - must have at least 1 contact

🔄 Data Structure Formats

The API supports two formats based on the unflatten parameter:

Nested Structure (unflatten=false or omitted)

When unflatten=false or not specified, use nested objects:
{
  "contact": {
    "number": "34666033135",
    "name": "Juan Pérez",
    "email": "[email protected]"
  },
  "components_push": {
    "header_text_0": "Welcome Message",
    "body_text_0": "Hello {{name}}",
    "body_text_1": "Your order is ready!",
    "footer_text_0": "Support Team"
  },
  "language_template": "es_ES",
  "name_template": "order_ready",
  "recipient_id": "34666033135",
  "notification_type": "transactional",
  "campaign_name": "Order Notifications"
}

Flat Structure (unflatten=true)

When unflatten=true, all nested objects must be flattened using dot notation:
{
  "contact.number": "34666033135",
  "contact.name": "Juan Pérez",
  "contact.email": "[email protected]",
  "components_push.header_text_0": "Welcome Message",
  "components_push.body_text_0": "Hello {{name}}",
  "components_push.body_text_1": "Your order is ready!",
  "components_push.footer_text_0": "Support Team",
  "language_template": "es_ES",
  "name_template": "order_ready",
  "recipient_id": "34666033135",
  "notification_type": "transactional",
  "campaign_name": "Order Notifications"
}

When to Use Each Format

  • Nested Structure (unflatten=false): Recommended for better readability and when your system supports nested objects
  • Flat Structure (unflatten=true): Use when your system doesn’t support nested objects or requires flat data structure

🎨 Components & Multimedia

IMPORTANT: Components you can send dynamically are ONLY those returned by the template components endpoint. The structure varies depending on whether the template has variables or media.

Component Naming Rules

  • Header media: header_image, header_video, header_document (NO suffix number)
  • Body variables: body_text_0, body_text_1, body_text_2, etc. (with index number for each {"{1}"}, {"{2}"}, {"{3}"} placeholder)
  • Footer and Buttons: Are STATIC in the template definition and CANNOT be sent dynamically

How to Know Which Components to Send

  1. First, call the template components endpoint:
    GET /notify/template/components?access_key={key}&template={template_id}
    
  2. The API returns only the components you need to provide:
    {
      "status": "OK",
      "components": ["header_image", "body_text_0", "body_text_1"]
    }
    
  3. Send ONLY those components in your notification request

Component Types by Position

Header Components (Media Only)

IMPORTANT: Only ONE header component per template. Header text is STATIC in the template.
TypeComponent NameDescriptionExample ValueSpecifications
imageheader_imageHeader image URL"https://example.com/image.jpg"JPG, PNG, GIF - Max 5MB
videoheader_videoHeader video URL"https://example.com/video.mp4"MP4, AVI, MOV - Max 16MB, 30s
documentheader_documentHeader document URL"https://example.com/doc.pdf"PDF, DOC, XLS, PPT - Max 100MB
Note: header_text is NOT a dynamic component. Header text is defined in the template and cannot be changed.

Body Components (Variables Only)

Body text with placeholders requires variables in order: {"{1}"}, {"{2}"}, {"{3}"}, etc.
Component NameDescriptionMaps to Template Placeholder
body_text_0First variable in body{"{1}"}
body_text_1Second variable in body{"{2}"}
body_text_2Third variable in body{"{3}"}
body_text_NN-th variable in body{"{N}"}
Example Template: “Hello {"{1}"}, your course {"{2}"} is ready”
  • body_text_0: Value for {"{1}"} (e.g., “Maria”)
  • body_text_1: Value for {"{2}"} (e.g., “JavaScript”)
⚠️ Footer is STATIC - defined in the template and cannot be modified per message.

Button Components

IMPORTANT: Most buttons are STATIC in the template. However, URL buttons with variables CAN be dynamic.
Component NameDescriptionWhen It Appears
buttons_url_0URL for first URL button with variable {"{1}"}When template has URL button with {"{1}"} placeholder
buttons_url_1URL for second URL button with variable {"{2}"}When template has multiple URL buttons with variables
Example Template Button: "url": "https://example.com/{"{1}"}"
  • The template defines the URL pattern with placeholder
  • You provide the value for {"{1}"} via buttons_url_0
Note: Quick reply buttons are always static and cannot be modified per message.

Component Examples

Example 1: Template with Image Header (Nested Structure)

{
  "components_push": {
    "header_image": "https://cdn.example.com/promo.jpg"
  }
}

Example 2: Template with Body Variables (Nested Structure)

{
  "components_push": {
    "body_text_0": "María",
    "body_text_1": "JavaScript Advanced Course"
  }
}

Example 3: Template with Image and Variables (Flat Structure)

{
  "components_push.header_image": "https://cdn.example.com/promo.jpg",
  "components_push.body_text_0": "María",
  "components_push.body_text_1": "JavaScript Advanced Course"
}

🧩 Template Variables (Body Text)

CRITICAL: Variables in WhatsApp templates use positional placeholders like {"{1}"}, {"{2}"}, {"{3}"}, NOT named variables like {"{name}"}.

How Template Variables Work

WhatsApp templates define variables as numbered placeholders in the template body text:
  • Template text: “Hello {"{1}"}, your course {"{2}"} is ready”
  • {"{1}"} maps to body_text_0
  • {"{2}"} maps to body_text_1

Variable Assignment

You provide the values for these numbered placeholders in the components_push object:
{
  "components_push": {
    "body_text_0": "María",
    "body_text_1": "JavaScript Advanced Course"
  }
}
Result: “Hello María, your course JavaScript Advanced Course is ready”

Important Rules

  1. Positional order matters: body_text_0 = {"{1}"}, body_text_1 = {"{2}"}, etc.
  2. Direct values: Provide the actual value, not {"{variable}"} syntax
  3. All variables required: Must provide values for all {"{N}"} placeholders in the template
  4. No mixing: Cannot combine variables with static text in components_push

✅ Valid Examples

Template: “Hello {"{1}"}, your order {"{2}"} is ready”
{
  "contact": {
    "name": "María",
    "order_id": "ORD-12345"
  },
  "components_push": {
    "body_text_0": "María",
    "body_text_1": "ORD-12345"
  }
}
You can also use contact fields directly:
{
  "contact": {
    "name": "María",
    "order_id": "ORD-12345"
  },
  "components_push": {
    "body_text_0": "{{name}}",
    "body_text_1": "{{order_id}}"
  }
}

❌ Invalid Examples

{
  "components_push": {
    "body_text_0": "Hello María", // ❌ Don't add template text, only the value
    "body_text_1": "{1}" // ❌ Don't use {1}, provide the actual value
  }
}

Variable Resolution

The system resolves {"{variable}"} references in components_push by searching:
  1. First in contact object
  2. Then in root-level parameters
{
  "contact": {
    "name": "María",
    "email": "[email protected]"
  },
  "order_id": "ORD-12345",
  "components_push": {
    "body_text_0": "{{name}}", // Resolved from contact.name → "María"
    "body_text_1": "{{order_id}}" // Resolved from root order_id → "ORD-12345"
  }
}

⏰ Scheduling & Conversation Flows

Immediate Delivery (Default)

If datetime_sending is not specified, the message is sent immediately:
{
  "contact": { "number": "34666033135" },
  "language_template": "es_ES",
  "name_template": "welcome_user",
  "recipient_id": "34666033135",
  "notification_type": "marketing",
  "campaign_name": "Welcome Campaign"
}

Scheduled Delivery

Use datetime_sending and timezone to schedule messages: Nested Structure:
{
  "contact": { "number": "34666033135" },
  "language_template": "es_ES",
  "name_template": "welcome_user",
  "recipient_id": "34666033135",
  "notification_type": "marketing",
  "campaign_name": "Welcome Campaign",
  "datetime_sending": "2024-12-25T10:30:00",
  "timezone": "Europe/Madrid"
}
Flat Structure:
{
  "contact.number": "34666033135",
  "language_template": "es_ES",
  "name_template": "welcome_user",
  "recipient_id": "34666033135",
  "notification_type": "marketing",
  "campaign_name": "Welcome Campaign",
  "datetime_sending": "2024-12-25T10:30:00",
  "timezone": "Europe/Madrid"
}

Conversation Flow Integration

Use name_event to trigger specific conversation flows:
{
  "contact": { "number": "34666033135" },
  "language_template": "es_ES",
  "name_template": "course_info",
  "recipient_id": "34666033135",
  "notification_type": "marketing",
  "campaign_name": "Course Enrollment",
  "name_event": "info-course-nursing"
}

📋 Complete Examples

Example 1: Single Recipient with Body Variables

Sending to ONE person using contact Template: “Hello {"{1}"}, thank you for your interest in {"{2}"}!” Request:
{
  "contact": {
    "number": "34666033135",
    "customer_name": "Sarah"
  },
  "components_push": {
    "body_text_0": "{{customer_name}}",
    "body_text_1": "our Premium Membership"
  },
  "language_template": "en",
  "name_template": "interest_confirmation",
  "recipient_id": "34666033135",
  "notification_type": "marketing",
  "campaign_name": "Membership Campaign 2024"
}
Response:
{
  "success": true,
  "message": "Registration successful",
  "notification_id": "uuid-notification-123"
}

Example 1B: Multiple Recipients (Bulk Sending)

Sending to MULTIPLE people using contacts Template: “Hello {"{1}"}, thank you for your interest in {"{2}"}!” Request:
{
  "contacts": [
    {
      "number": "34666033135",
      "customer_name": "Sarah"
    },
    {
      "number": "34777044246",
      "customer_name": "John"
    },
    {
      "number": "34888055357",
      "customer_name": "Michael"
    }
  ],
  "components_push": {
    "body_text_0": "{{customer_name}}",
    "body_text_1": "our Premium Membership"
  },
  "language_template": "en",
  "name_template": "interest_confirmation",
  "recipient_id": "bulk_campaign",
  "notification_type": "marketing",
  "campaign_name": "Membership Campaign 2024"
}
Response:
{
  "success": true,
  "message": "Processing completed",
  "total": 3,
  "processed": 3,
  "failed": 0,
  "errors": []
}
If some contacts fail:
{
  "success": true,
  "message": "Processing completed",
  "total": 3,
  "processed": 2,
  "failed": 1,
  "errors": [
    {
      "contact": { "number": "34888055357", "customer_name": "Michael" },
      "reason": "Invalid phone number format"
    }
  ]
}

Example 2: Template with Image Header

Template components: ["header_image"] URL:
POST https://cloud.lovi.ai/functions/v1/notify?access_key=your-api-key
Nested Structure:
{
  "contact": {
    "number": "34666033135",
    "customer_name": "Robert",
    "email": "[email protected]"
  },
  "components_push": {
    "header_image": "https://cdn.example.com/seasonal-promotion.jpg"
  },
  "language_template": "en",
  "name_template": "seasonal_offer",
  "recipient_id": "34666033135",
  "notification_type": "marketing",
  "campaign_name": "Holiday Sale 2024",
  "datetime_sending": "2024-12-24T09:00:00",
  "timezone": "Europe/Madrid"
}
Note: Body, footer, and buttons are static in the template and cannot be modified.

Example 3: Template with PDF Document

Template components: ["header_document"] URL:
POST https://cloud.lovi.ai/functions/v1/notify?access_key=your-api-key&unflatten=true
Flat Structure:
{
  "contact.number": "34666033135",
  "contact.customer_name": "Jennifer",
  "contact.email": "[email protected]",
  "components_push.header_document": "https://files.example.com/statement-2024.pdf",
  "language_template": "en",
  "name_template": "document_delivery",
  "recipient_id": "34666033135",
  "notification_type": "transactional",
  "campaign_name": "Monthly Statements"
}

Example 4: Conversation Flow Trigger

Nested Structure:
{
  "contact": {
    "number": "34666033135",
    "first_name": "Laura",
    "last_name": "Martínez",
    "course_interest": "Web Development"
  },
  "language_template": "en_US",
  "name_template": "course_inquiry",
  "recipient_id": "34666033135",
  "notification_type": "marketing",
  "campaign_name": "Course Enrollment Q1",
  "name_event": "course-web-development-info",
  "datetime_sending": "2024-12-26T10:00:00",
  "timezone": "Europe/Madrid"
}

📊 Response Codes

Successful Response (200 OK)

Immediate sending:
{
  "success": true,
  "message": "Notification queued successfully",
  "notification_id": "uuid-notification-123"
}
Scheduled sending:
{
  "success": true,
  "message": "Notification scheduled successfully",
  "notification_id": "uuid-notification-456",
  "scheduled_time": "2024-12-25T10:30:00Z"
}

Error Responses

400 Bad Request - Invalid Parameters

{
  "error": "validation_failed",
  "message": "Required field missing",
  "details": {
    "field": "contact.number",
    "reason": "This field is required"
  }
}

401 Unauthorized - Invalid Access Key

{
  "error": "unauthorized",
  "message": "Invalid or expired access key"
}

404 Not Found - Template Not Found

{
  "error": "template_not_found",
  "message": "Template 'welcome_user' not found or not approved"
}

422 Unprocessable Entity - Business Logic Error

{
  "error": "template_not_approved",
  "message": "Template is not in approved status",
  "details": {
    "template_name": "pending_template",
    "current_status": "pending"
  }
}

429 Too Many Requests - Rate Limit

{
  "error": "rate_limit_exceeded",
  "message": "Too many requests. Try again in 60 seconds",
  "retry_after": 60
}

🔧 Best Practices

Data Structure

  • Prefer nested structure (unflatten=false) for better readability
  • Use flat structure (unflatten=true) only when your system requires it
  • Validate structure before sending requests

Media Guidelines

  • Use HTTPS URLs for all media files
  • Optimize file sizes for faster delivery
  • Use public URLs without authentication requirements
  • Test media URLs before sending to ensure accessibility

Scheduling

  • Specify timezone when using datetime_sending
  • Validate future dates before scheduling
  • Consider business hours for better engagement
  • Test scheduling in development environment

Placeholders

  • Use meaningful variable names that match your data
  • Test variable substitution before production
  • Keep one variable per field to avoid errors
  • Provide fallback values in your application logic

Performance

  • Batch multiple notifications when possible
  • Cache template information to reduce API calls
  • Monitor rate limits and implement backoff strategies
  • Use connection pooling for better performance

🚨 Common Errors & Solutions

Contact/Contacts Validation Errors

Error: Missing Contact Information

Problem: Request doesn’t include contact or contacts
{
  "error": "validation_failed",
  "message": "Missing required field: 'contact' or 'contacts'"
}
Solution: You must include either contact (for one person) OR contacts (for multiple people)

Error: Using Both Contact and Contacts

Problem: Request includes both contact and contacts at the same time
{
  "error": "validation_failed",
  "message": "Cannot use both 'contact' and 'contacts' simultaneously"
}
Solution: Choose only one - use contact for single recipient or contacts for multiple recipients

Error: Contact is a List Instead of Object

Problem: Sent contact as a list [...] instead of an object {...}
{
  "error": "validation_failed",
  "message": "'contact' must be an object, not an array"
}
Solution: For single recipient, use contact as an object:
{
  "contact": {
    "number": "34666033135",
    "name": "John"
  }
}

Error: Contacts is Not a List

Problem: Sent contacts as an object {...} instead of a list [...]
{
  "error": "validation_failed",
  "message": "'contacts' must be an array, not an object"
}
Solution: For multiple recipients, use contacts as a list:
{
  "contacts": [
    { "number": "34666033135", "name": "John" },
    { "number": "34777044246", "name": "Sarah" }
  ]
}

Error: Empty Contacts List

Problem: Sent contacts as an empty list []
{
  "error": "validation_failed",
  "message": "'contacts' array cannot be empty"
}
Solution: Include at least one contact in the list

Error: Too Many Contacts

Problem: Sent more than 100 contacts in the contacts list
{
  "error": "validation_failed",
  "message": "Maximum 100 contacts allowed per request. Received: 150"
}
Solution: Split your contacts into multiple requests of maximum 100 contacts each

Error: Unflatten with Contacts

Problem: Used unflatten=true with contacts (bulk sending)
{
  "error": "validation_failed",
  "message": "Parameter 'unflatten' is incompatible with 'contacts' array"
}
Solution: Bulk sending with contacts only works with nested structure. Remove unflatten=true or use unflatten=false

Structure Mismatch

Problem: Mixing nested and flat structures Solution: Choose one format consistently based on unflatten parameter

Template Not Found

Problem: Using non-existent or non-approved template Solution: Verify template name and approval status using template management endpoints

Invalid Phone Format

Problem: Including ’+’ or spaces in phone number Solution: Use clean international format without symbols (e.g., 34666033135)

Component Naming Errors

Problem: Using incorrect component names like header_image_0 instead of header_image Solution: Always check template components first using the /notify/template/components endpoint

Variable Position Errors

Problem: Incorrect mapping between template placeholders and body_text components Solution: Remember {"{1}"} = body_text_0, {"{2}"} = body_text_1, etc.

Modifying Static Components

Problem: Trying to send footer, buttons, or header text dynamically Solution: These components are static in the template. Only send components returned by the template components endpoint

Scheduling Errors

Problem: Past dates or invalid timezone Solution: Use future dates in ISO 8601 format with valid IANA timezone codes

🔬 Template Examples

These examples demonstrate different types of template structures.

Example 1: Simple Text Only Template

Components returned by API: [] (empty - no dynamic components)
{
  "contact": {
    "number": "34666033135"
  },
  "language_template": "en",
  "name_template": "welcome_message",
  "recipient_id": "34666033135",
  "notification_type": "marketing",
  "campaign_name": "Welcome Campaign"
}
Note: No components_push needed since template has no dynamic components.

Example 2: Template with Body Variables

Template Body: “Hello {"{1}"}, your {"{2}"} is ready!” Components returned by API: ["body_text_0", "body_text_1"]
{
  "contact": {
    "number": "34666033135",
    "customer_name": "John",
    "product_name": "Premium Package"
  },
  "components_push": {
    "body_text_0": "{{customer_name}}",
    "body_text_1": "{{product_name}}"
  },
  "language_template": "en",
  "name_template": "order_ready",
  "recipient_id": "34666033135",
  "notification_type": "transactional",
  "campaign_name": "Order Notifications"
}
Result: “Hello John, your Premium Package is ready!”

Example 3: Template with Image Header

Components returned by API: ["header_image"]
{
  "contact": {
    "number": "34666033135"
  },
  "components_push": {
    "header_image": "https://example.com/promotional-banner.jpg"
  },
  "language_template": "en",
  "name_template": "promo_with_image",
  "recipient_id": "34666033135",
  "notification_type": "marketing",
  "campaign_name": "Holiday Sale"
}
Note: Body, footer, and buttons are static in the template.

Example 4: Template with Video Header

Components returned by API: ["header_video"]
{
  "contact": {
    "number": "34666033135"
  },
  "components_push": {
    "header_video": "https://example.com/product-demo.mp4"
  },
  "language_template": "en",
  "name_template": "video_announcement",
  "recipient_id": "34666033135",
  "notification_type": "marketing",
  "campaign_name": "Product Launch"
}

Example 5: Template with PDF Document

Components returned by API: ["header_document"]
{
  "contact": {
    "number": "34666033135",
    "customer_name": "Jane Smith"
  },
  "components_push": {
    "header_document": "https://example.com/invoice-12345.pdf"
  },
  "language_template": "en",
  "name_template": "invoice_delivery",
  "recipient_id": "34666033135",
  "notification_type": "transactional",
  "campaign_name": "Monthly Invoices"
}

Example 6: Template with Header Text and Body Variable

Header: “Important Notice” (static text in template) Body: “Hello {"{1}"}, welcome to our platform!” Components returned by API: ["body_text_0"]
{
  "contact": {
    "number": "34666033135",
    "first_name": "Michael"
  },
  "components_push": {
    "body_text_0": "{{first_name}}"
  },
  "language_template": "en",
  "name_template": "welcome_with_header",
  "recipient_id": "34666033135",
  "notification_type": "utility",
  "campaign_name": "User Onboarding"
}
Result:
  • Header: “Important Notice”
  • Body: “Hello Michael, welcome to our platform!”

Example 7: Template with Dynamic URL Button

Body: Static text Buttons:
  • URL button with variable: "url": "https://example.com/{"{1}"}" (dynamic)
  • Quick reply button: “Contact Support” (static) Components returned by API: ["buttons_url_0"]
{
  "contact": {
    "number": "34666033135",
    "customer_id": "12345"
  },
  "components_push": {
    "buttons_url_0": "https://example.com/profile/{{customer_id}}"
  },
  "language_template": "en",
  "name_template": "account_notification",
  "recipient_id": "34666033135",
  "notification_type": "utility",
  "campaign_name": "Account Updates"
}
Result: The URL button will link to https://example.com/profile/12345 Note: The variable in buttons_url_0 can reference contact fields using {"{variable}"} syntax, and the system will resolve it before sending.