Skip to content

folk API: contactField values on Deals (custom objects) are accepted with 200 but silently stored as []

Using the folk.app REST API (api.folk.app/v1, API version 2025-06-09 docs, Oct 2026) to sync a fundraise pipeline: a group with the Deals object type, plus a custom field created via POST /v1/groups/{groupId}/custom-fields/Deals with {"type":"contactField","name":"Intro Path"}. Creating or updating a deal with PATCH /v1/groups/{groupId}/Deals/{objectId} and body {"customFieldValues":{"Intro Path":[{"id":"per_00000000-0000-4000-a000-000000000000"}]}} returns 200, but the response (and later GETs) show "Intro Path": []. Other field types on the same deal (singleSelect, textField, numericField, dateField, userField) persist fine. Tried a person that is linked to the deal and in the same group, and a person in another group: both dropped. Passing a bare id string returns 422 'Expected object, received string'; adding entityType returns 422 'You must only provide a contact id.', so the {id} shape is what the validator wants. The core-concepts custom-fields page says contactField stores 'a reference to other contacts in the same workspace' without restricting entity type.

1 solution
ranked by outcome — not votes
Accepted

The Deals/custom-object endpoints do not support writing contactField values. The update-an-object and create-an-object OpenAPI specs list value formats for textField, numericField, dateField, singleSelect, multipleSelect, and userField only, while update-a-person and update-a-company also list contactField ([{"id": "per_..."}]) and objectField. The object endpoint validates the shape and then discards the value.

Workaround: put the contactField on the group's company (or person) entity instead, where customFieldValues: {"<groupId>": {"Intro Path": [{"id": "per_..."}]}} persists, and mirror the names into a textField on the deal if you need them visible in the pipeline view. There is no API endpoint to delete a group custom field, so an unusable contactField created on Deals has to be removed in the folk UI.

Related quirks hit during the same setup: numericField create needs config: {"format": "number", "decimals": 1} (omitting format gives 422 invalid_union_discriminator, options currency|none|default|number|percent); PATCH custom field removeOptions takes option id strings (["gcfo_..."]), not [{"id": ...}]; folk's RateLimit-Policy has a 20 req/2s burst on top of 600/60s, so concurrent clients need to pace both windows.