> ## Documentation Index
> Fetch the complete documentation index at: https://docs.vambe.me/llms.txt
> Use this file to discover all available pages before exploring further.

# Update metadata to an AI contact

## Overview

Update or add custom metadata fields to a contact. Metadata allows you to store any additional information about a contact beyond the standard fields (name, email, phone).

This endpoint is intelligent - it processes unstructured data and automatically fills custom field definitions if they exist in your account. Field keys are automatically converted to snake\_case for consistency.

## Use Cases

* **E-commerce Data**: Store order history, lifetime value, preferred products
* **Customer Segmentation**: Add custom attributes for targeting and personalization
* **CRM Integration**: Sync custom fields from external CRM systems
* **Lead Scoring**: Store qualification scores, lead source, conversion probability
* **Business Context**: Add company info, industry, decision maker status
* **Behavioral Data**: Track user actions, preferences, interaction history
* **Support Information**: Store account tier, support plan, SLA requirements

## Authentication

This endpoint requires authentication using an API key. Include your API key in the request header:

```
x-api-key: your_api_key_here
```

## Path Parameters

| Parameter     | Type          | Required | Description                      |
| ------------- | ------------- | -------- | -------------------------------- |
| `aiContactId` | string (UUID) | Yes      | Unique identifier of the contact |

## Request Body

The request body is a flexible object where you can include any key-value pairs:

| Field   | Type | Required | Description                        |
| ------- | ---- | -------- | ---------------------------------- |
| `{any}` | any  | No       | Any custom field as key-value pair |

You can send any metadata fields as a JSON object. Common examples:

```json theme={null}
{
  "company": "Acme Corp",
  "industry": "Technology",
  "employeeCount": "500-1000",
  "annualRevenue": "5000000",
  "leadScore": "85",
  "source": "website",
  "interests": "AI, Automation",
  "custom_field_1": "value1"
}
```

## Response Structure

The endpoint returns an array of updated custom field values:

| Field | Type  | Description                                           |
| ----- | ----- | ----------------------------------------------------- |
| Array | array | Array of custom field value objects that were updated |

Each item in the response array contains information about the updated field.

## Example Request

```bash theme={null}
curl --request POST \
  'https://api.vambe.me/api/public/contact/1937bd2c-a13c-4365-af06-43af9a988e36/update-metadata' \
  --header 'Content-Type: application/json' \
  --header 'x-api-key: your_api_key_here' \
  --data-raw '{
    "company": "Acme Corporation",
    "position": "CEO",
    "industry": "Technology",
    "employeeCount": "100-500",
    "annualRevenue": "2000000",
    "leadScore": 95,
    "leadSource": "LinkedIn Campaign",
    "interestedProducts": "Enterprise Plan, API Access",
    "notes": "Very interested in automation features",
    "budget": "50000-100000",
    "decisionMaker": true,
    "expectedCloseDate": "2024-12-31"
  }'
```

## Example Response

```json theme={null}
[
  {
    "id": 123,
    "key": "company",
    "value": "Acme Corporation",
    "custom_field_definition_id": 45
  },
  {
    "id": 124,
    "key": "position",
    "value": "CEO",
    "custom_field_definition_id": 46
  },
  {
    "id": 125,
    "key": "lead_score",
    "value": "95",
    "custom_field_definition_id": 47
  }
]
```

## Common Use Cases

### 1. Update E-commerce Customer Data

```javascript theme={null}
const updateEcommerceData = async (contactId, orderData) => {
  const response = await fetch(
    `https://api.vambe.me/api/public/contact/${contactId}/update-metadata`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': 'your_api_key_here',
      },
      body: JSON.stringify({
        totalOrders: orderData.orderCount.toString(),
        lifetimeValue: orderData.totalSpent.toString(),
        averageOrderValue: orderData.averageValue.toString(),
        lastPurchaseDate: orderData.lastPurchase,
        favoriteCategory: orderData.topCategory,
        customerSince: orderData.firstOrderDate,
        vipStatus: orderData.isVIP ? 'Yes' : 'No',
      }),
    },
  );

  const result = await response.json();
  console.log(`Updated ${result.length} metadata fields`);
  return result;
};
```

### 2. Lead Qualification Data

```javascript theme={null}
const updateLeadQualification = async (contactId, qualificationData) => {
  const response = await fetch(
    `https://api.vambe.me/api/public/contact/${contactId}/update-metadata`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': 'your_api_key_here',
      },
      body: JSON.stringify({
        leadScore: qualificationData.score,
        leadGrade: qualificationData.grade, // A, B, C, D
        qualificationDate: new Date().toISOString(),
        budgetRange: qualificationData.budget,
        timeframe: qualificationData.timeframe, // Immediate, 1-3 months, 3-6 months
        authority: qualificationData.isDecisionMaker
          ? 'Decision Maker'
          : 'Influencer',
        need: qualificationData.painPoints,
        source: qualificationData.leadSource,
      }),
    },
  );

  return await response.json();
};
```

### 3. Sync from External CRM

```javascript theme={null}
const syncFromExternalCRM = async (contactId, crmData) => {
  // Map CRM fields to Vambe metadata
  const metadata = {
    crmContactId: crmData.id,
    accountType: crmData.account_type,
    industry: crmData.industry,
    companySize: crmData.employee_count,
    annualRevenue: crmData.revenue,
    territory: crmData.sales_territory,
    accountOwner: crmData.owner_name,
    lastActivityDate: crmData.last_activity,
    stage: crmData.sales_stage,
    probability: crmData.win_probability,
  };

  const response = await fetch(
    `https://api.vambe.me/api/public/contact/${contactId}/update-metadata`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': 'your_api_key_here',
      },
      body: JSON.stringify(metadata),
    },
  );

  return await response.json();
};
```

### 4. Add Support Plan Information

```javascript theme={null}
const updateSupportPlan = async (contactId, planDetails) => {
  const response = await fetch(
    `https://api.vambe.me/api/public/contact/${contactId}/update-metadata`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': 'your_api_key_here',
      },
      body: JSON.stringify({
        supportPlan: planDetails.tier, // Basic, Premium, Enterprise
        planStartDate: planDetails.startDate,
        planExpiryDate: planDetails.expiryDate,
        slaLevel: planDetails.sla, // 24h, 12h, 4h
        dedicatedManager: planDetails.hasManager ? 'Yes' : 'No',
        maxTicketsPerMonth: planDetails.ticketLimit.toString(),
        supportChannels: planDetails.channels.join(', '), // Email, Phone, Chat
      }),
    },
  );

  return await response.json();
};
```

### 5. Update Form Submission Data

```javascript theme={null}
const saveFormSubmission = async (contactId, formData) => {
  const response = await fetch(
    `https://api.vambe.me/api/public/contact/${contactId}/update-metadata`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': 'your_api_key_here',
      },
      body: JSON.stringify({
        formName: formData.formType,
        submissionDate: new Date().toISOString(),
        utmSource: formData.utm_source,
        utmMedium: formData.utm_medium,
        utmCampaign: formData.utm_campaign,
        referrer: formData.referrer,
        landingPage: formData.landingPage,
        message: formData.userMessage,
      }),
    },
  );

  return await response.json();
};
```

### 6. Bulk Update Multiple Contacts

```javascript theme={null}
const bulkUpdateMetadata = async (contactIds, metadata) => {
  const results = [];

  for (const contactId of contactIds) {
    try {
      const response = await fetch(
        `https://api.vambe.me/api/public/contact/${contactId}/update-metadata`,
        {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            'x-api-key': 'your_api_key_here',
          },
          body: JSON.stringify(metadata),
        },
      );

      const result = await response.json();
      results.push({ contactId, success: true, fieldsUpdated: result.length });
    } catch (error) {
      results.push({ contactId, success: false, error: error.message });
    }

    // Add small delay to avoid rate limiting
    await new Promise((resolve) => setTimeout(resolve, 100));
  }

  return results;
};

// Usage: Apply same metadata to multiple contacts
bulkUpdateMetadata(['contact-id-1', 'contact-id-2'], {
  campaignParticipant: 'Summer Sale 2024',
  enrolled: new Date().toISOString(),
});
```

## Key Behavior & Features

### Automatic Snake Case Conversion

Field keys are automatically converted to snake\_case:

**You send**: `{ "companyName": "Acme Corp" }`
**Stored as**: `{ "company_name": "Acme Corp" }`

**You send**: `{ "LeadScore": "95" }`
**Stored as**: `{ "lead_score": "95" }`

### Intelligent Field Processing

The endpoint uses AI to process unstructured data and match it to your custom field definitions:

* If a custom field definition exists for a key, it will be used
* Data types are automatically formatted based on field definitions
* Missing fields are created as needed
* All fields are updated (not merged)

### Update Behavior

* **Additive**: New metadata fields are added to existing ones
* **Overwrite**: If a field already exists, its value is updated
* **Flexible Types**: Values can be strings, numbers, booleans, or objects (converted to strings)
* **No Validation**: You can send any fields - they don't need to be pre-defined

## Data Type Handling

Metadata values should generally be sent as strings for consistency:

| Data Type You Send | How It's Stored     | Example                |
| ------------------ | ------------------- | ---------------------- |
| String             | As is               | `"Acme Corp"`          |
| Number             | Converted to string | `95` → `"95"`          |
| Boolean            | Converted to string | `true` → `"true"`      |
| Object             | JSON string         | Converted to JSON      |
| Array              | Joined string       | `["a","b"]` → `"a, b"` |

**Best Practice**: Send everything as strings for predictable behavior.

## Error Responses

| Status Code | Description                                  |
| ----------- | -------------------------------------------- |
| 400         | Bad Request - Invalid metadata format        |
| 401         | Unauthorized - Invalid or missing API key    |
| 404         | Not Found - Contact not found                |
| 500         | Internal Server Error - Something went wrong |

## Important Notes

* **No Field Limit**: You can send as many metadata fields as needed
* **Size Limit**: Very large metadata objects may be rejected (keep under 1MB)
* **Field Names**: Use descriptive, consistent naming (prefer snake\_case or camelCase)
* **Retrieve Metadata**: Use GET `/api/public/contact/{aiContactId}/info` to see all metadata
* **Type Coercion**: All values are ultimately stored as strings in custom fields
* **Activity Logging**: Metadata updates are logged in contact activity

## Best Practices

### 1. Use Consistent Field Names

```javascript theme={null}
// Good: Consistent naming convention
const goodMetadata = {
  leadSource: 'Website',
  leadScore: '85',
  companyName: 'Acme Corp',
  isQualified: 'true',
};

// Avoid: Inconsistent naming
const badMetadata = {
  'Lead Source': 'Website', // Has spaces
  LeadScore: '85', // Mixed case
  company_name: 'Acme Corp', // Different convention
  qualified: 'true', // Missing is prefix
};
```

### 2. Include Timestamps

```javascript theme={null}
const metadataWithTimestamps = {
  lastUpdated: new Date().toISOString(),
  dataSource: 'API Import',
  importBatch: 'batch-2024-09-30',
  // ... other fields
};
```

### 3. Validate Before Sending

```javascript theme={null}
const validateAndUpdateMetadata = (contactId, metadata) => {
  // Remove null/undefined values
  const cleanMetadata = Object.entries(metadata).reduce((acc, [key, value]) => {
    if (value !== null && value !== undefined && value !== '') {
      acc[key] = String(value); // Ensure all values are strings
    }
    return acc;
  }, {});

  return fetch(
    `https://api.vambe.me/api/public/contact/${contactId}/update-metadata`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': 'your_api_key_here',
      },
      body: JSON.stringify(cleanMetadata),
    },
  );
};
```

### 4. Handle Errors Gracefully

```javascript theme={null}
const safeUpdateMetadata = async (contactId, metadata) => {
  try {
    const response = await fetch(
      `https://api.vambe.me/api/public/contact/${contactId}/update-metadata`,
      {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'x-api-key': 'your_api_key_here',
        },
        body: JSON.stringify(metadata),
      },
    );

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}: ${await response.text()}`);
    }

    return await response.json();
  } catch (error) {
    console.error('Failed to update metadata:', error);
    // Maybe retry or log to error tracking
    throw error;
  }
};
```

## Related Endpoints

* [GET /api/public/contact/{aiContactId}/info](/reference/contact/get-info-of-an-ai-contact) - Get contact info including metadata
* [POST /api/public/ticket/open/web-whatsapp/{phoneId}](/reference/ticket/open-ticket-web-whatsapp) - Create ticket with metadata
* [PATCH /api/public/contact/tags/{aiContactId}](/reference/tags/update-contact-tags) - Update contact tags

## Complete Workflow Example

```javascript theme={null}
// Complete workflow: Create ticket with metadata, then update it
const createAndEnrichContact = async (phoneNumber, initialData) => {
  // 1. Create ticket/contact
  const createResponse = await fetch(
    'https://api.vambe.me/api/public/ticket/open/web-whatsapp/your_phone_id',
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': 'your_api_key_here',
      },
      body: JSON.stringify({
        to_phone_number: phoneNumber,
        stage_id: 'your_stage_id',
        contact_name: initialData.name,
        contact_metadata: {
          source: initialData.source,
        },
      }),
    },
  );

  const { aiContactId } = await createResponse.json();

  // 2. Enrich with additional metadata after qualification
  const enrichResponse = await fetch(
    `https://api.vambe.me/api/public/contact/${aiContactId}/update-metadata`,
    {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'x-api-key': 'your_api_key_here',
      },
      body: JSON.stringify({
        company: initialData.company,
        industry: initialData.industry,
        employeeCount: initialData.size,
        leadScore: calculateLeadScore(initialData),
        enrichedAt: new Date().toISOString(),
        dataQuality: 'High',
      }),
    },
  );

  const metadata = await enrichResponse.json();

  console.log(`Created and enriched contact ${aiContactId}`);
  console.log(`Updated ${metadata.length} metadata fields`);

  return { aiContactId, metadata };
};
```

## Metadata vs Tags

| Feature      | Metadata (This Endpoint)          | Tags                        |
| ------------ | --------------------------------- | --------------------------- |
| **Purpose**  | Store detailed data               | Categorize/label contacts   |
| **Format**   | Key-value pairs                   | Simple labels               |
| **Quantity** | Unlimited fields                  | Multiple tags               |
| **Search**   | By field name and value           | By tag name                 |
| **Use Case** | Customer details, scores, history | Segments, categories, flags |

Use **metadata** for detailed information and **tags** for simple categorization.


## OpenAPI

````yaml post /api/public/contact/{aiContactId}/update-metadata
openapi: 3.0.0
info:
  title: Vambe AI API
  description: Vambe AI documentation
  version: '1.0'
  contact: {}
servers:
  - url: https://api.vambe.me
    description: Production Server
security: []
tags:
  - name: Vambe AI
    description: ''
paths:
  /api/public/contact/{aiContactId}/update-metadata:
    post:
      tags:
        - Contact
      summary: Update metadata to an AI contact
      operationId: PublicAiContactController_addMetadataToAiContact
      parameters:
        - name: x-api-key
          in: header
          description: API key required to authorize the request
          required: true
          schema:
            type: string
        - name: aiContactId
          required: true
          in: path
          description: ID of the AI contact
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                metadata:
                  type: object
      responses:
        '200':
          description: Metadata added successfully.
        '404':
          description: AI contact not found.

````