# Custom fields

Define typed fields on contacts, such as plan, city or renewal date, set them from your app or a workflow, and use them in segments and messages.

Custom fields add your own data to [contacts](https://pozy.ai/docs/inbox/contacts): a customer's plan, their city, the date their trial ends. You define a field once per profile, with a type, then set values per contact. Fields feed broadcast segments, workflow conditions and message personalisation.

## Define a field

```bash cURL
curl -X POST https://api.pozy.ai/v1/custom-fields \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profileId": "6650c1e2a3b4c5d6e7f80911",
    "name": "Plan",
    "slug": "plan",
    "type": "select",
    "options": ["free", "pro", "team"]
  }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/custom-fields", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    profileId: "6650c1e2a3b4c5d6e7f80911",
    name: "Plan",
    slug: "plan",
    type: "select",
    options: ["free", "pro", "team"],
  }),
});
const data = await res.json();
if (!res.ok) throw new Error(data.error);
console.log(data.field.id, data.field.slug);
```
```python Python
import os
import requests

resp = requests.post(
    "https://api.pozy.ai/v1/custom-fields",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    json={"profileId": "6650c1e2a3b4c5d6e7f80911", "name": "Plan", "slug": "plan", "type": "select", "options": ["free", "pro", "team"]},
    timeout=30,
)
print(resp.json()["field"])
```

| Field | Notes |
| --- | --- |
| `profileId` | Required. Fields belong to one profile |
| `name` | Required. Shown in the dashboard |
| `slug` | The key you use in the API and in templates. Generated from the name when you leave it out |
| `type` | Required: `text`, `number`, `date`, `boolean` or `select` |
| `options` | The allowed values for `select` fields |

The type is fixed once the field exists. `PATCH /v1/custom-fields/{fieldId}` can rename it or change `options`, not the type. To change the type, create a new field and copy the values.

## Set and clear values

`PUT /v1/contacts/{contactId}/fields/{slug}` sets or overwrites one value. The value must match the field's type: a number for `number`, `true` or `false` for `boolean`, an ISO date for `date`, one of the `options` for `select`.

```bash cURL
curl -X PUT https://api.pozy.ai/v1/contacts/6650c1e2a3b4c5d6e7f80c44/fields/plan \
  -H "Authorization: Bearer $POZY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "value": "pro" }'
```
```ts Node
const res = await fetch("https://api.pozy.ai/v1/contacts/6650c1e2a3b4c5d6e7f80c44/fields/plan", {
  method: "PUT",
  headers: {
    Authorization: `Bearer ${process.env.POZY_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ value: "pro" }),
});
if (!res.ok) throw new Error((await res.json()).error);
```
```python Python
requests.put(
    "https://api.pozy.ai/v1/contacts/6650c1e2a3b4c5d6e7f80c44/fields/plan",
    headers={"Authorization": f"Bearer {os.environ['POZY_API_KEY']}"},
    json={"value": "pro"},
    timeout=30,
)
```

`DELETE /v1/contacts/{contactId}/fields/{slug}` clears the value. Deleting the field definition with `DELETE /v1/custom-fields/{fieldId}` removes its values from every contact.

Values come back on every contact under `customFields`, keyed by slug.

## Where fields are used

| Where | How |
| --- | --- |
| Broadcast segments | `segmentFilters.customFields` matches contacts holding exact values, for example `{ "plan": "pro" }`. Types must match too: `5` does not match `"5"`. See [Broadcasts](https://pozy.ai/docs/automations/broadcasts) |
| Workflows | `{{contact.fields.plan}}` in any message, `condition` rules on it, and `set_field` nodes to write it. See [Workflow nodes](https://pozy.ai/docs/automations/workflow-nodes) |

## Field changes as webhooks

`contact.field_changed` fires whenever a value actually changes, with `field`, `previousValue`, `value` and `source` (`api`, `workflow` or `automation`). Writing the value a contact already has fires nothing. Use it to mirror fields into your CRM without polling.

## Endpoints

| Endpoint | What it does |
| --- | --- |
| [`PUT /v1/contacts/{contactId}/fields/{slug}`](https://pozy.ai/docs/api/setContactFieldValue) | Set custom field value |
| [`DELETE /v1/contacts/{contactId}/fields/{slug}`](https://pozy.ai/docs/api/clearContactFieldValue) | Clear custom field value |
| [`GET /v1/custom-fields`](https://pozy.ai/docs/api/listCustomFields) | List custom field definitions |
| [`POST /v1/custom-fields`](https://pozy.ai/docs/api/createCustomField) | Create custom field |
| [`PATCH /v1/custom-fields/{fieldId}`](https://pozy.ai/docs/api/updateCustomField) | Update custom field |
| [`DELETE /v1/custom-fields/{fieldId}`](https://pozy.ai/docs/api/deleteCustomField) | Delete custom field |

## Webhook events

| Event | When it fires |
| --- | --- |
| [`contact.field_changed`](https://pozy.ai/docs/webhooks/events#contact-field-changed) | Contact field changed. |
