> For the complete documentation index, see [llms.txt](https://flapjax.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://flapjax.gitbook.io/docs/rest-api/reference/people-events/batch-update-people.md).

# Batch Update People

## Batch update people

> Patches up to \*\*100 people\*\* in a single request. Each item must include an \`external\_id\` to identify the person. Only the provided fields are updated (PATCH semantics), and each item may carry \`consents\` to answer individual consent purposes.<br>

```json
{"openapi":"3.0.3","info":{"title":"Listener API","version":"1.0.0"},"tags":[{"name":"batch-update-people"}],"servers":[{"url":"https://{domain}/v1","variables":{"domain":{"default":"your-domain","description":"Your deployment domain"}}}],"security":[{"bearerAuth":[]}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token with the required scope for the endpoint. Pass via the `Authorization: Bearer <token>` header.\n"}},"schemas":{"UpdatePersonRequest":{"type":"object","description":"Request body for updating or patching a person. For `PUT`, omitted fields are cleared. For `PATCH`, omitted fields are left unchanged.\n","properties":{"external_id":{"type":"string","description":"New external identifier."},"first_name":{"type":"string","description":"First name."},"last_name":{"type":"string","description":"Last name."},"email":{"type":"string","format":"email","description":"Email address."},"mobile_prefix":{"type":"string","description":"Mobile phone country prefix."},"mobile_number":{"type":"string","description":"Mobile phone number."},"date_of_birth":{"type":"string","description":"Date of birth."},"language":{"type":"string","description":"Preferred language code."},"email_consent":{"type":"boolean","description":"Email channel consent. This is a summary of the person's consent answers on the email channel: `false` refuses every consent purpose on the channel, `true` grants only the purpose marked as that channel's default and leaves the other purposes on their own defaults. Use `consents` to answer individual purposes.\n"},"sms_consent":{"type":"boolean","description":"SMS channel consent. Behaves like `email_consent` on the SMS channel.\n"},"push_notification_consent":{"type":"boolean","description":"Push notification consent. A plain flag — consent purposes only apply to the `email` and `sms` channels.\n"},"consents":{"type":"array","description":"Individual consent answers written alongside the person's own fields. Each entry answers one consent purpose on one channel.\n\nCells are applied **after** the person update succeeds. An invalid cell fails the request with **400** — a consent type that is unknown or archived, one that is mandatory (mandatory purposes can never be refused, so they cannot be answered), or one that does not apply to the given channel.\n\nOmitted purposes are left untouched: a purpose the person has never answered keeps resolving to the consent type's default state.\n","items":{"$ref":"#/components/schemas/PersonConsentAssignment"}},"attribute_values":{"type":"object","description":"Custom attribute key-value pairs. Relationship attributes are normalized and auto-created.\n","additionalProperties":true}}},"PersonConsentAssignment":{"type":"object","description":"One consent answer: this consent purpose, on this channel, in this state. Consent types are configured per organization; their numeric IDs come from your organization's consent settings.\n","required":["consent_type_id","channel","state"],"properties":{"consent_type_id":{"type":"integer","minimum":1,"description":"Identifier of the consent type being answered. Must reference an active, non-mandatory consent type on your organization.\n"},"channel":{"type":"string","description":"Channel the answer applies to. The consent type must be configured for this channel.\n","enum":["email","sms"]},"state":{"type":"boolean","description":"The answer. `true` grants the purpose on the channel, `false` refuses it. Writing an answer replaces any previous answer, and the channel summary flag (`email_consent` / `sms_consent`) is recomputed from the stored answers.\n"}}},"BatchResult":{"type":"object","description":"Result of a single record in a batch operation.","properties":{"index":{"type":"integer","description":"Zero-based index of the record in the request array."},"success":{"type":"boolean","description":"Whether the operation succeeded for this record."},"error":{"type":"string","nullable":true,"description":"Error message if the operation failed (null on success)."}}},"ErrorResponse":{"type":"object","description":"Standard error response envelope.","properties":{"error":{"type":"string","description":"Machine-readable error type. Use this for programmatic error handling.\n","enum":["operation_failed","failed_binding_json","invalid_json","invalid_parameter","not_found","duplicate_entry","rate_limit_exceeded"]},"errorstack":{"type":"object","nullable":true,"description":"Detailed error stack (only present for server errors)."}}}}},"paths":{"/people/batch":{"patch":{"summary":"Batch update people","operationId":"batchUpdatePeople","tags":["batch-update-people"],"description":"Patches up to **100 people** in a single request. Each item must include an `external_id` to identify the person. Only the provided fields are updated (PATCH semantics), and each item may carry `consents` to answer individual consent purposes.\n","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","minItems":1,"maxItems":100,"items":{"$ref":"#/components/schemas/UpdatePersonRequest"}}}}},"responses":{"200":{"description":"Batch processed.","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/BatchResult"}}}}},"400":{"description":"Request body is invalid or empty.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ErrorResponse"}}}}}}}}}
```
