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

# Send Contact to Splitter

> Send a contact into a splitter, which routes them to one of its underlying campaigns.

Send a contact to a splitter and let it pick which campaign to enroll them in. This uses the same `POST /contacts` endpoint as [Create Contact](/api-reference/contacts/create) — the only difference is that you send `splitterId` instead of `campaignId`.

All splitter management (create, update, stats, distribution strategies) is handled by admins in the dashboard. This endpoint is the only one integrators need.

## Request Body

<ParamField body="phoneNumber" type="string" required>
  Phone number in E.164 format (e.g., `+15551234567`).
</ParamField>

<ParamField body="splitterId" type="string (UUID)" required>
  The ID of the splitter to route this contact through.

  <Note>
    Send either `splitterId` **or** `campaignId`, not both. If both are provided, `campaignId` takes priority and the splitter is ignored.
  </Note>
</ParamField>

<ParamField body="name" type="string">
  Full name of the contact.
</ParamField>

<ParamField body="customVariables" type="object">
  Flat key-value pairs of custom data to store on the contact. All values must be **strings** — send numbers as `"50000"`, not `50000`.

  These variables are passed to the assigned campaign and available for template substitution in SMS, WhatsApp templates, and voice agent prompts as `{{company}}`, `{{leadSource}}`, etc.
</ParamField>

## Authentication

Either method works:

* **JWT token:** `Authorization: Bearer <jwt-token>`
* **API key:** `X-API-Key: <api-key>` — rate limited to 30 writes per minute.

## What happens after the contact is created

1. The splitter picks a campaign based on its distribution strategy (percentage, time-weighted, or adaptive).
2. The contact is created in the chosen campaign with all provided `customVariables`.
3. The distribution decision is logged (visible in the splitter's Distribution Logs in the dashboard).
4. The campaign's schedule kicks in — calls, SMS, and WhatsApp are scheduled based on that campaign's configuration.
5. `customVariables` are available for template substitution in all outbound messages.

## Existing contacts

If a contact with the same phone number already exists for this admin:

* **If active:** the contact is updated with the new name and `customVariables`, then reassigned to the splitter's chosen campaign. Existing scheduled calls, SMS, and WhatsApp are cancelled and rescheduled.
* **If soft-deleted:** the contact is reactivated with the new data and assigned to the splitter's chosen campaign.

This matches the existing behavior for direct `campaignId` contact creation.

## Response

<ResponseField name="success" type="boolean">
  Always `true` on a successful request.
</ResponseField>

<ResponseField name="data" type="object">
  The created or updated contact object.

  <Expandable title="Contact fields">
    <ResponseField name="id" type="string (UUID)">
      Unique identifier for the contact.
    </ResponseField>

    <ResponseField name="adminId" type="string (UUID)">
      The admin (team) the contact belongs to.
    </ResponseField>

    <ResponseField name="campaignId" type="string (UUID)">
      The campaign the splitter assigned this contact to. The contact now lives in this campaign and follows its schedule normally.
    </ResponseField>

    <ResponseField name="phoneNumber" type="string">
      Phone number in E.164 format.
    </ResponseField>

    <ResponseField name="name" type="string">
      Contact name.
    </ResponseField>

    <ResponseField name="timezone" type="string">
      IANA timezone string.
    </ResponseField>

    <ResponseField name="status" type="string">
      Contact status. Newly created contacts start as `active`.
    </ResponseField>

    <ResponseField name="customVariables" type="object">
      Custom key-value data stored on the contact.
    </ResponseField>

    <ResponseField name="createdAt" type="string (ISO 8601)">
      Timestamp of when the contact was created.
    </ResponseField>

    <ResponseField name="updatedAt" type="string (ISO 8601)">
      Timestamp of the last update.
    </ResponseField>
  </Expandable>
</ResponseField>

## Errors

| Status | Error                                                                | When                                                            |
| ------ | -------------------------------------------------------------------- | --------------------------------------------------------------- |
| `400`  | `Splitter distribution failed: Splitter has no active campaigns`     | All campaigns in the splitter are deactivated.                  |
| `400`  | `Splitter distribution failed: No valid campaigns found in splitter` | All campaigns in the splitter were deleted.                     |
| `400`  | `Invalid phone number`                                               | Phone number format is invalid or not in allowed country codes. |
| `404`  | `Splitter not found or inactive`                                     | Splitter doesn't exist, was deleted, or is paused.              |
| `429`  | Rate limit exceeded                                                  | More than 30 requests/minute with API key auth.                 |

## Zapier

The Zapier endpoint also supports splitters:

```
POST /contacts/zapier
```

Same fields, same behavior. Custom variables can be sent as flat top-level fields — Zapier transform middleware will nest them into `customVariables` automatically.

<RequestExample>
  ```bash cURL theme={null}
  curl -X POST https://api.guayaba.ai/contacts \
    -H "Content-Type: application/json" \
    -H "X-API-Key: gua_a1b2c3d4_your-api-key-here" \
    -d '{
      "phoneNumber": "+15551234567",
      "name": "Jane Smith",
      "splitterId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "customVariables": {
        "company": "Acme Corp",
        "leadSource": "website",
        "loanAmount": "50000"
      }
    }'
  ```

  ```javascript Node.js theme={null}
  const response = await fetch('https://api.guayaba.ai/contacts', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': 'gua_a1b2c3d4_your-api-key-here'
    },
    body: JSON.stringify({
      phoneNumber: '+15551234567',
      name: 'Jane Smith',
      splitterId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
      customVariables: {
        company: 'Acme Corp',
        leadSource: 'website',
        loanAmount: '50000'
      }
    })
  });

  const data = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.guayaba.ai/contacts',
      headers={
          'Content-Type': 'application/json',
          'X-API-Key': 'gua_a1b2c3d4_your-api-key-here'
      },
      json={
          'phoneNumber': '+15551234567',
          'name': 'Jane Smith',
          'splitterId': 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
          'customVariables': {
              'company': 'Acme Corp',
              'leadSource': 'website',
              'loanAmount': '50000'
          }
      }
  )

  data = response.json()
  ```
</RequestExample>

<ResponseExample>
  ```json 201 Created theme={null}
  {
    "success": true,
    "data": {
      "id": "d290f1ee-6c54-4b01-90e6-d701748f0851",
      "adminId": "8f14e45f-ceea-467a-9575-d0e9e3c8b8a3",
      "campaignId": "550e8400-e29b-41d4-a716-446655440000",
      "phoneNumber": "+15551234567",
      "name": "Jane Smith",
      "timezone": "America/New_York",
      "status": "active",
      "customVariables": {
        "company": "Acme Corp",
        "leadSource": "website",
        "loanAmount": "50000"
      },
      "createdAt": "2026-05-13T14:30:00.000Z",
      "updatedAt": "2026-05-13T14:30:00.000Z"
    }
  }
  ```

  ```json 400 Bad Request theme={null}
  {
    "error": "Bad Request",
    "message": "Splitter distribution failed: Splitter has no active campaigns"
  }
  ```

  ```json 404 Not Found theme={null}
  {
    "error": "Not found",
    "message": "Splitter not found or inactive"
  }
  ```
</ResponseExample>
