#Data Minimization and Field Selection
Status: Available. The
fields=parameter works when reading conversations, messages, and leads in all cells.
Purpose: how to get only the fields you need, and why personal data does not arrive until you name it explicitly.
#Contents
- The rule in one line
- Where it works
- Syntax
- Which fields are personal data
- Examples
- Errors
- Disclosure log and the JWE format
- Recommendations
#The rule in one line
- Without
fields=, you receive all fields of the object except personal ones. - A personal data field arrives only if you name it in
fields=. - The
idfield always arrives.
This way, an integration that needs statuses and identifiers does not receive names, email addresses, phone numbers, or correspondence text, and therefore does not store or forward them. This is data minimization: you request exactly what the task needs.
#Where it works
| Method | Scope | Object |
|---|---|---|
GET /conversations | conversations:read | conversation |
GET /conversations/{id} | conversations:read | conversation |
GET /conversations/{id}/messages | conversations:read | message |
GET /leads | leads:read | lead |
GET /leads/{id} | leads:read | lead |
The parameter is the same for all five methods and for all cells (RU, EN, ID).
#Syntax
fields is a single string of comma-separated names, up to 2000 characters and up to 64 names. Spaces around names are discarded.
| What you named | What you receive |
|---|---|
| nothing (the parameter is absent or empty) | all fields except personal data |
an ordinary field, for example status | id and that field |
a top-level personal data field, for example content | id and that field |
a nested path, for example contact.email | id and the contact object with only that personal data field (the other contact fields that are not personal data remain) |
a whole object, for example contact | the object without its own personal data fields; name the personal data inside separately: contact,contact.email |
If you name at least one field, the other fields do not arrive: the response is limited to what you named and id. To get "everything ordinary plus email", list the ordinary fields and add contact.email.
#Which fields are personal data
| Object | Personal data fields |
|---|---|
| Conversation | external_customer_id |
| Message | content, attachments |
| Lead | contact.first_name, contact.last_name, contact.email, contact.phone, contact.geo_country, contact.geo_city, title, intent, ai_summary, score.reason, next_action, lost_reason |
What is not personal data: identifiers, statuses, modes, dates, contact.id, contact.tags, the source and attribution tags, the numeric score score.score and score.grade, the amount, and the message cards (product cards contain no customer data).
The free text of a lead (title, intent, ai_summary, next_action, lost_reason, score.reason) is also on the list: it is written by people and by the model, and it contains names, phone numbers, and email addresses. Internal staff notes are never returned externally.
#Examples
Only the statuses and modification times of conversations, with no personal data:
curl -sS "$API_BASE_URL/conversations?limit=50&fields=status,mode,updated_at" \
-H "Authorization: Bearer $API_KEY"
{
"data": [
{ "id": "6f1c2c0e-0000-4000-8000-000000000001", "status": "open", "mode": "ai", "updated_at": "2026-10-12T09:15:00.000Z" }
],
"next_cursor": null
}
A lead with email but without the other personal data:
curl -sS "$API_BASE_URL/leads/$LEAD_ID?fields=status,value,contact,contact.email" \
-H "Authorization: Bearer $API_KEY"
{
"id": "9a7d4e10-0000-4000-8000-000000000002",
"status": "qualified",
"value": { "amount": 4900, "currency": "RUB" },
"contact": { "id": "3b9e1f22-0000-4000-8000-000000000003", "tags": [], "email": "[email protected]" }
}
Messages with text (personal data named explicitly):
curl -sS "$API_BASE_URL/conversations/$CONVERSATION_ID/messages?fields=sender,created_at,content" \
-H "Authorization: Bearer $API_KEY"
The same messages without fields= arrive without content and attachments: the sender, type, cards, and time remain.
#Errors
An unknown name is not skipped silently but returns 422 validation_failed with the /fields field and the unknown_field code:
{
"type": "https://formula-cream.pro/developers/errors#validation_failed",
"title": "The request failed validation",
"status": 422,
"code": "validation_failed",
"request_id": "req_EXAMPLE0004",
"errors": [
{ "field": "/fields", "code": "unknown_field", "message": "Unknown field \"emial\"." }
]
}
A list that is too long (more than 64 names) gives the same 422 with the reason code too_many. Repeating the request unchanged is pointless: fix the request.
#Disclosure log and the JWE format
- Only the fields that were actually disclosed are recorded in the personal data disclosure log (Cells and Data). A request without personal data fields adds nothing to the log.
- In the RU cell, the
conversations:*andleads:*scopes apply only when the key has a narrow list of allowed IP addresses and the recipient's country is RU; otherwise403 pii_transfer_not_allowedarrives before any read, andfields=does not change this (Transferring personal data). Minimization reduces how much personal data you receive, but it does not lift the admission conditions. - In the EN and ID cells, the personal data fields you name may arrive in the JWE format (JWE format for personal data fields).
#Recommendations
- Always pass
fields=in background syncs: they rarely need the correspondence text. - Request a personal data field only at the moment it is needed, and do not write it to your system's logs.
- To walk through large lists, take
idandupdated_at, and request personal data one object at a time. - Do not build logic on the absence of a field: a field that is missing from the response is not the same as
null.nullmeans "there is no value", while absence means "you did not request it".