Where It Applies
Single-row upserts use
POST /v1/contacts with a different body shape and the higher-rate contacts.write_single policy (100 / min). Single-row deletes use DELETE /v1/contacts/{email}. Choose the shape that matches your workload: 100/min × 1 row = 100 contacts/min; 10/min × 1000 rows = 10,000 contacts/min.
Partial-Success Contract
The HTTP status is200 even when some rows fail. Per-row failures are surfaced in errors[] so callers can iterate, fix, and re-submit just the bad rows.
POST /v1/contacts: Batch Upsert
Up to 1000 rows per request. email is the merge key; missing or malformed emails land in errors[]. Custom fields auto-create field definitions on the brand on first sight. See Pagination for reading them back.
200):
TypeScript SDK
POST /v1/contacts/batch-delete: Batch Delete
Up to 1000 emails per request. The endpoint is idempotent: deleting a non-existent email surfaces in notFound[], not as an error.
200):
Common Per-Row Error Codes
Foundational codes from Errors (e.g.
RATE_LIMITED, INVALID_REQUEST) only ever apply to the whole request, never to individual rows.
Auto-Created Field Definitions
When a batch upsert references a custom column that doesn’t have afieldDefinitions row on the brand yet, the helper auto-creates one BEFORE the write. The new column shows up immediately in:
- the contacts page (filterable / sortable / searchable),
- the audience builder,
GET /v1/fields,POST /v1/contacts/searchwith{ "logic": "and", "filters": [{ "field": "plan", "operator": "equals", "value": "pro" }] }.
normalizeAudienceFieldName so snake_case + kebab-case + spaces all converge on the same camelCase column. Normalization events surface in warnings[] as FIELD_NAME_NORMALIZED.
Partial Success Design
Batch writes report aggregate counts and failed rows so invalid data does not block valid contacts. The contract and response shape are above; see API design notes for the rationale. If your workflow requires all-or-nothing, validate and stage the input before calling the endpoint. Do not infer which successful rows were newly inserted from aggregate counts and delete them, because a batch can also update existing contacts.See Also
- Idempotency: set
Idempotency-Keyon the batch so a retry doesn’t replay 1000 rows. - Rate limits:
contacts.write_batchis 10/min (vs single at 100/min); plan accordingly. - Pagination: for reading back the imported rows.
- Errors: full error catalog for the foundational request-level codes.
Need Help?
Our team is ready to support you at every step of your journey with Brew. Choose the option that works best for you:- Self-Service Tools
- Talk to Our Team
Search Documentation
Type in the “Ask any question” search bar at the top left to instantly find relevant documentation pages.
ChatGPT/Claude Integration
Click “Open in ChatGPT” at the top right of any page to explore it further with ChatGPT or Claude.