Custom Fields API
Extend the contact schema with typed fields specific to your business —lifetime_value, plan_tier, last_login_at, account_manager, etc. Custom fields are referenced from segment rules, flow conditions, message personalization, and the inbox UI.
Base path: /api/v1/custom-fields
Authentication: API key (X-API-Key) or session JWT.
Using the SDKs
client.request escape hatch defines a field and sets it on a contact (source-only; vendor it from packages/sdk-python):
ApiResponse envelope. See the SDK index at SDK quickstart.
Definitions
A field’s
key and type are immutable: they are fixed at creation and cannot be changed by PATCH (changing either would orphan the values already stored on contacts). To switch a field’s type, delete it and create a new one.
Create a definition
POST /api/v1/custom-fields/ creates the definition and returns it with the server-assigned id, plus any options (required for select / multiselect) and validators you sent:
List definitions
GET /api/v1/custom-fields/ returns every definition in display order — display_order ascending, never-ordered fields trailing, then by key. A freshly provisioned tenant returns an empty list.
Update a definition
PATCH /api/v1/custom-fields/{id} renames the field or updates its description, options, default value, required/searchable flags, and validators. key and type stay frozen — pass null on a validator or the default value to clear it.
Reorder definitions
Pass the list’s definition ids top-to-bottom; the response returns the refreshed list so you can reconcile in one round-trip. Unknown ids are ignored, omitted ids keep their current position.Dependencies and explorer
Before you delete or repurpose a field, check what references it.Dependency scan
GET /api/v1/custom-fields/{id}/dependencies returns the count of contacts holding a value plus the segments, campaigns, drip enrollments, and flows that reference the field — the same snapshot the 409 below carries on a guarded delete:
Explorer payload
GET /api/v1/custom-fields/{id}/explorer returns the definition, the usage block (the same counts as the scan above, plus a total), and up to ten of the most recently written values — every sample is PII-redacted and tail-truncated to 120 characters:
Deleting a field that is in use
DELETE is guarded so you don’t silently break the segments, campaigns, and flows that reference a field.
If the field is still referenced by any segment, campaign, or flow — or has values stored on any contact — the request returns 409 Conflict with code CUSTOM_FIELD_IN_USE. The response details list every dependent so you can review or clean up first:
?force=true:
force=true the delete proceeds: the field’s values are stripped from every contact, and each dependent is flagged so you can find it afterward. Dependent segments are set to status: "broken". Dependent campaigns keep running — an in-flight send is never interrupted — but are flagged with a broken_custom_fields marker in their metadata. Dependent flows are set to status: "broken" so the automation runner skips them. The reference to the deleted field is not stripped from the campaign or flow filter: the filter JSON is left intact and only flagged, so the stale field-leaf remains until you edit or rebuild the filter. A field that is not referenced anywhere deletes without needing force.
Values
Per-contact values are written via a single endpoint and read inline as part of the contact resource.
The value is validated against the definition — a wrong type or a violate-the-validator value returns
422. A successful write echoes the contact id and key with ok: true, and the next contact GET reflects the new value because the write invalidates the contact-detail cache:
custom_fields object — keyed by the field key:
Example — define a lifetime-value field, then set it
See also
- Contacts API — read custom-field values inline