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

# Create or Update Contact

> Insert a new contact or update an existing one based on email or phone

<Tip>
  This endpoint performs an **upsert**: it first tries to match by email, then falls back to phone if no email match is
  found. If no match is found, a new contact is created.
</Tip>

## How Matching Works

The endpoint matches contacts in this order:

<Steps>
  <Step title="Match by email">
    If `email` is provided, searches for an existing contact with the same email in your company.
  </Step>

  <Step title="Match by phone">If no email match is found and `phone` is provided, searches by phone number.</Step>
  <Step title="Create new">If no match is found, creates a new contact.</Step>
</Steps>

<Info>
  Need to update a contact's email? Prefer **[Update by ID](/api-reference/core-data/contacts/update-by-id)** for a
  deterministic update. Upsert only avoids creating a new record if a phone number is also provided and matches an
  existing contact.
</Info>

## Custom Fields

Store additional data using the `customFields` object. Fields are flexible key-value pairs:

```json theme={null}
{
  "email": "customer@example.com",
  "customFields": {
    "company": "Acme Corp",
    "plan": "enterprise",
    "signup_date": "2025-11-13"
  }
}
```

When updating an existing contact, custom fields are **deep merged** — only the fields you include are changed, existing fields are preserved.

## Marketing Consent

Control channel-specific marketing consent when creating or updating a contact:

```json theme={null}
{
  "email": "customer@example.com",
  "marketingConsent": {
    "email": true,
    "whatsapp": false
  }
}
```

| Field      | Type    | Description                                             |
| ---------- | ------- | ------------------------------------------------------- |
| `email`    | boolean | Email marketing opt-in (`true`) or opt-out (`false`)    |
| `whatsapp` | boolean | WhatsApp marketing opt-in (`true`) or opt-out (`false`) |

<Info>
  Both fields are optional. Omitting a channel leaves its current consent status unchanged. Setting `true` opts the
  contact in, `false` opts them out.
</Info>

## Phone Numbers

For WhatsApp messaging, include a phone number in **E.164 format**:

```json theme={null}
{
  "email": "customer@example.com",
  "phone": "+393391234567"
}
```

**Format requirements**:

* Include country code (e.g., `+39` for Italy, `+1` for US)
* No spaces, dashes, or parentheses
* Example: `+12025551234` (US), `+393391234567` (Italy)

<Warning>Invalid phone numbers will cause WhatsApp message sending to fail.</Warning>

## Common Errors

| Error                  | Cause                     | Solution                               |
| ---------------------- | ------------------------- | -------------------------------------- |
| `validation_error`     | Invalid email format      | Check email address format             |
| `invalid_phone_number` | Phone not in E.164 format | Add country code, remove spaces        |
| `rate_limit_exceeded`  | Too many requests         | Implement rate limiting, use batching  |
| `unauthorized`         | Invalid API key           | Verify API key in Authorization header |

## Related Endpoints

* **[Get Contact by Email](/api-reference/core-data/contacts/get-by-email)**: Look up a contact by email
* **[Update Contact by ID](/api-reference/core-data/contacts/update-by-id)**: Update a specific contact by ID
* **[Delete Contact](/api-reference/core-data/contacts/delete)**: Soft-delete a contact
* **[Custom Fields](/api-reference/core-data/custom-fields/list)**: Manage custom field definitions


## OpenAPI

````yaml post /public/v1/contacts
openapi: 3.1.0
info:
  title: API Documentation
  description: Documentation for transactional and subscribe APIs
  version: 1.0.0
servers:
  - url: https://app.minimo.it
security:
  - bearerAuth: []
paths:
  /public/v1/contacts:
    servers:
      - url: https://api.minimo.it
    post:
      summary: Insert or update a contact
      operationId: insertNewContact
      requestBody:
        required: true
        content:
          application/json:
            schema:
              oneOf:
                - type: object
                  properties:
                    email:
                      type: string
                      description: Email address of the contact
                    phone:
                      type: string
                      description: Phone number of the contact
                    customFields:
                      type: object
                      additionalProperties: true
                      description: Custom fields specific to the contact
                    marketingConsent:
                      $ref: '#/components/schemas/MarketingConsent'
                  required:
                    - email
                - type: array
                  items:
                    type: object
                    properties:
                      email:
                        type: string
                        description: Email address of the contact
                      phone:
                        type: string
                        description: Phone number of the contact
                      customFields:
                        type: object
                        additionalProperties: true
                        description: Custom fields specific to the contact
                      marketingConsent:
                        $ref: '#/components/schemas/MarketingConsent'
                    required:
                      - email
              examples:
                - email: email@minimo.it
                  customFields:
                    customKey1: customValue1
                    customKey2: customValue2
                    customKey3: customValue3
                  marketingConsent:
                    email: true
                    whatsapp: false
          multipart/form-data:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                  description: Email address of the contact
                phone:
                  type: string
                  description: Phone number of the contact
                customFields.*:
                  type: string
                  description: Custom field key-value pairs for the contact
              examples:
                - email: email@minimo.it
                  customFields.customKey1: customValue1
                  customFields.customKey2: customValue2
                  customFields.customKey3: customValue3
          application/x-www-form-urlencoded:
            schema:
              type: object
              properties:
                email:
                  type: string
                  format: email
                  description: Email address of the contact
                phone:
                  type: string
                  description: Phone number of the contact
                customFields.*:
                  type: string
                  description: Custom field key-value pairs for the contact
              examples:
                - email: email@minimo.it
                  customFields.customKey1: customValue1
                  customFields.customKey2: customValue2
                  customFields.customKey3: customValue3
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: array
                items:
                  type: object
                  properties:
                    contactId:
                      type: string
                      examples:
                        - '12345'
        '400':
          description: Bad request
          content:
            application/json:
              schema:
                type: object
                properties:
                  error:
                    type: string
                    examples:
                      - Invalid email format
      security:
        - bearerAuth: []
      x-codeSamples:
        - lang: Python
          source: >-
            import requests


            url = 'https://api.minimo.it/public/v1/contacts'

            headers = {'Authorization': 'Bearer {{BEARER_TOKEN}}',
            'Content-Type': 'application/json'}

            data = {
                'email': '{{email}}',
                'customFields': {
                    '{{customKey1}}': '{{customValue1}}',
                    '{{customKey2}}': '{{customValue2}}',
                    '{{customKey3}}': '{{customValue3}}'
                },
                'marketingConsent': {
                    'email': True,
                    'whatsapp': False
                }
            }

            response = requests.post(url, json=data, headers=headers)

            print(response.json())
        - lang: JavaScript
          source: >-
            fetch('https://api.minimo.it/public/v1/contacts', {
                method: 'POST',
                headers: {
                    'Authorization': 'Bearer {{BEARER_TOKEN}}',
                    'Content-Type': 'application/json'
                },
                body: JSON.stringify({
                    'email': '{{email}}',
                    'customFields': {
                        '{{customKey1}}': '{{customValue1}}',
                        '{{customKey2}}': '{{customValue2}}',
                        '{{customKey3}}': '{{customValue3}}'
                    },
                    'marketingConsent': {
                        'email': true,
                        'whatsapp': false
                    }
                })
            }).then(response => response.json()).then(data =>
            console.log(data));
        - label: CLI
          lang: cURL
          source: |-
            curl --request POST \
              --url https://api.minimo.it/public/v1/contacts \
              --header 'Authorization: Bearer {{BEARER_TOKEN}}' \
              --header 'Content-Type: application/json' \
              --data '{
              "email": "{{email}}",
              "phone": "{{phone}}",
              "customFields": {
                "{{customKey1}}": "{{customValue1}}",
                "{{customKey2}}": "{{customValue2}}",
                "{{customKey3}}": "{{customValue3}}"
              },
              "marketingConsent": {
                "email": true,
                "whatsapp": false
              }
             }'
        - lang: PHP
          source: |
            <?php

            $curl = curl_init();

            curl_setopt_array($curl, array(
                CURLOPT_URL => 'https://api.minimo.it/public/v1/contacts',
                CURLOPT_RETURNTRANSFER => true,
                CURLOPT_ENCODING => '',
                CURLOPT_MAXREDIRS => 10,
                CURLOPT_TIMEOUT => 0,
                CURLOPT_FOLLOWLOCATION => true,
                CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
                CURLOPT_CUSTOMREQUEST => 'POST',
                CURLOPT_POSTFIELDS =>'{
                    "email": "{{email}}",
                    "customFields": {
                        "{{customKey1}}": "{{customValue1}}",
                        "{{customKey2}}": "{{customValue2}}",
                        "{{customKey3}}": "{{customValue3}}"
                    }
                }',
                CURLOPT_HTTPHEADER => array(
                    'Authorization: Bearer {{BEARER_TOKEN}}',
                    'Content-Type: application/json'
                ),
            ));

            $response = curl_exec($curl);

            curl_close($curl);
            echo $response;
        - lang: Java
          source: |-
            import java.net.HttpURLConnection;
            import java.net.URL;
            import java.io.OutputStream;
            import java.io.InputStreamReader;
            import java.io.BufferedReader;

            public class Main {
                public static void main(String[] args) {
                    try {
                        URL url = new URL('https://api.minimo.it/public/v1/contacts');
                        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                        conn.setRequestMethod('POST');
                        conn.setRequestProperty('Authorization', 'Bearer {{BEARER_TOKEN}}');
                        conn.setRequestProperty('Content-Type', 'application/json');
                        conn.setDoOutput(true);
                        String jsonInputString = '{"email": "{{email}}", "customFields": {"{{customKey1}}": "{{customValue1}}", "{{customKey2}}": "{{customValue2}}", "{{customKey3}}": "{{customValue3}}"}}';
                        try(OutputStream os = conn.getOutputStream()) {
                            byte[] input = jsonInputString.getBytes('utf-8');
                            os.write(input, 0, input.length);
                        }
                        try(BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), 'utf-8'))) {
                            StringBuilder response = new StringBuilder();
                            String responseLine = null;
                            while ((responseLine = br.readLine()) != null) {
                                response.append(responseLine.trim());
                            }
                            System.out.println(response.toString());
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        - lang: HTML
          source: |-
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <meta name="viewport" content="width=device-width, initial-scale=1.0">
                <title>Subscribe New Contact</title>
            </head>
            <body>
                <h1>Subscribe New Contact</h1>
                <form action="https://api.minimo.it/public/v1/contacts" method="POST">
                    <label for="email">Email:</label>
                    <input type="email" id="email" name="email" required><br><br>

                    <label for="phone">Phone:</label>
                    <input type="text" id="phone" name="phone"><br><br>

                    <label for="customField1">Custom Field 1:</label>
                    <input type="text" id="customField1" name="customFields.customField1"><br><br>

                    <label for="customField2">Custom Field 2:</label>
                    <input type="text" id="customField2" name="customFields.customField2"><br><br>

                    <label for="customField3">Custom Field 3:</label>
                    <input type="text" id="customField3" name="customFields.customField3"><br><br>

                    <button type="submit">Subscribe</button>
                </form>
            </body>
            </html>
components:
  schemas:
    MarketingConsent:
      type: object
      description: Channel-specific marketing consent preferences
      properties:
        email:
          type: boolean
          description: Email marketing consent (true = opted in, false = opted out)
          example: true
        whatsapp:
          type: boolean
          description: WhatsApp marketing consent (true = opted in, false = opted out)
          example: false
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: mn-API_CLIENT_ID-API_KEY

````