Documentation
Sign inGet started

Create or update contacts in bulk

POST
/v1/contacts/batch
const result = await bird.contacts.batch({
  contacts: [{ email: "jane@acme.com", first_name: "Jane" }],
});
for (const item of result.data) {
  console.log(item.entry.email, item.status);
}
Response200
{
  "data": [
    {
      "matched_on": "email",
      "status": "created",
      "contact_id": "con_01krdgeqcxet5s7t44vh8rt9mg"
    }
  ]
}
Creates or updates up to 1,000 contacts in one request. Each entry is matched automatically against every identifier it supplies: its email address (trimmed and lowercased), its phone number (normalized to international form), and your own external_id. An entry with no match creates a contact. An entry whose identifiers all match one contact updates the supplied fields and preserves omitted fields. This lets an email address change under a stable external_id without creating a second contact. An entry whose identifiers belong to several contacts fails with an error naming each match; contacts are never merged automatically. Supplying match_on makes that field the only matching key, and every entry must include it. You can also add every contact in the request to up to 10 audiences.
Each entry succeeds or fails on its own: the response lists one result per contact in submission order (created, updated, or failed with the reason), and a failed entry does not abort the rest. If the request itself is invalid, for example when an entry in audience_ids does not exist, the whole request fails with a validation error and no contacts are written.
Request Payload
contacts
array of object
required
Contacts to create or update, matched automatically against every identifier an entry supplies. Existing contacts are updated with the fields each entry supplies; omitted fields keep their stored values, so an entry can set fields but never clear them. Unmatched entries create contacts.
Show child parameters
contacts.email
string
The contact's email address. Trimmed and lowercased before it is stored and checked for uniqueness. Unique within the workspace. Supply an email address, a phone number, or both.
contacts.phone_number
string
The contact's phone number in E.164 format, including the leading + and country code. Spaces and punctuation are accepted and stripped; the number is stored in its canonical form, which may differ from what you send, and is unique within the workspace. An empty string is treated as if the field were omitted. Supply an email address, a phone number, or both.
contacts.first_name
string
The contact's first name.
contacts.last_name
string
The contact's last name.
contacts.external_id
string
Your own identifier for this contact, such as a user ID in your system. Unique within the workspace when set.
contacts.data
object
Custom property values for this contact. Each key must be an active contact property. Each value must match the property's declared type: string, number, boolean, or RFC 3339 datetime. Strings can contain up to 500 characters, and a null value is ignored. Unregistered or archived keys return a validation error. The serialized data is limited to 2 KB.
audience_ids
array of string
Audiences every contact in this request is added to. Contacts that are already members are left in place. Every listed audience must exist, or the whole request fails with a validation error and nothing is written.
match_on
string
Optional field used to match every entry to an existing contact. Every entry must include this field when set. When omitted, each entry is matched against all identifiers it supplies. No match creates a contact, one match updates it, and identifiers that match multiple contacts return an error naming each contact.
Possible values: email, phone_number, external_id
data_mode
string
How a supplied data object is applied to an existing contact. The default merge mode adds the supplied keys to the contact's stored custom values. A key with a null value deletes that key. The replace mode overwrites the whole stored data map with the supplied map. In both modes a contact that omits data keeps its stored values unchanged, so an import that touches one attribute never wipes the others.
Possible values: merge, replace
Response Payload
data
array of object
required
One entry per contact in the request, in submission order.
Show child attributes
data.entry
object
required
The identifiers a batch entry supplied, in the normalized form used for matching. A field is null when the entry did not include it. These values identify the request entry and do not represent the contact's current state.
Show child attributes
data.entry.email
nullable string
required
Email address this entry carried, trimmed and lowercased. null when the entry carried none.
data.entry.phone_number
nullable string
required
Phone number this entry carried, in its normalized international form. null when the entry carried none. A row rejected for an invalid phone echoes the value as sent, trimmed, since no normalized form exists.
data.entry.external_id
nullable string
required
Your own identifier for this entry, when the entry supplied one.
data.matched_on
nullable string
required
Which identifier matched this entry to an existing contact. null when the entry created a new contact.
Possible values: email, phone_number, external_id, null
data.status
string
required
What happened to this contact.
  • created: a new contact was created for the address.
  • updated: an existing contact with the address was updated.
  • failed: the entry was rejected and error explains why. A failed entry does not affect the other entries in the request.
Possible values: created, updated, failed
data.contact_id
string
ID of the created or updated contact. Absent when the entry failed.
data.error
object
Why this entry failed. Absent for successful entries.
Show child attributes
data.error.type
string
required
Machine-readable error category for this entry, such as validation_error or conflict_error, in the same vocabulary as the top-level error type. New categories may be added over time, so treat unrecognized values as a generic failure.
data.error.code
string
required
Specific error code for this entry, from the same catalog as the top-level error code. E04058 means the entry matched two contacts and requires review. E04055 means the phone number belongs to another contact and you must retry with different data. Both are conflict_error errors; the code distinguishes them.
data.error.message
string
required
Human-readable explanation of why this entry failed.