Goal
By the end of this guide you will be able to perform full CRUD operations on contacts: retrieve your contact structure (to get field IDs), create new contacts, retrieve them individually, update their fields or status, and delete them.Prerequisites
- An API key with
contacts:writescope (for full CRUD) orcontacts:readscope (for read-only access) - Your API base URL (found on the Account Settings > API Keys page)
About contact status
Every contact has astatus field that tracks whether they’re reachable. The shape depends on the contact’s state.
Active contacts have only a primary status:
- Status values are case-sensitive.
Active,Inactive, andUnsubscribemust use exact capitalization. Lowercase values such as"active"are rejected with a validation error. - Customers can only set
Unsubscribeas the secondary value via the API. This is the only customer-initiated reason for inactivating a contact. Other secondary values appear on contacts the system has inactivated automatically based on email events, and cannot be set through API calls. The values you may see on contacts the system has inactivated include:Bounce— the contact’s email address hard-bouncedComplaint-FBL— the contact’s mailbox provider reported a spam complaint via the Feedback Loop (FBL) mechanism
- When transitioning a contact to Inactive, both
primaryandsecondaryare required. Sending onlyprimary: "Inactive"returns a validation error. - Once a contact is Inactive, it cannot be reactivated via the API. This is a deliberate compliance protection — customers who unsubscribe or hard-bounce should not be re-emailed. To re-add a contact, they must opt in again through your sign-up flow.
Steps
1. Fetch your contact structure
Before creating contacts, retrieve your contact structure to get the field IDs you will need. Every contact belongs to a contact structure, and field values are set by referencing each field’s_id.
200 OK):
_id and the field _id values. You will use these when creating and updating contacts. See Manage Custom Fields for more details on contact structures.
2. Create a contact
Send aPOST request with the contact’s email address (key), the contact structure it belongs to, and optionally any custom field values and list assignments.
200 OK):
accountId, createdBy, and modifiedBy fields, which are auto-populated by the system and read-only. They’re omitted from the samples here for clarity.
Key points:
keyis the contact’s email address (required)contactStructureIdis required — it determines which custom fields are availablefieldsuses the field_idfrom your contact structure (see Step 1)listsoptionally assigns the contact to one or more lists at creation time- New contacts start with
__v: 0. You’ll need this version value when updating the contact via PUT.
3. Get a contact by ID
Retrieve a single contact using its_id.
200 OK):
__v field is the contact’s current version. You’ll need this exact value when updating the contact via PUT (see Step 5).
4. Update a contact’s status
UsePATCH to update a contact’s status. The most common case is unsubscribing a contact:
200 OK):
PATCHonly accepts thestatusfield. To update other fields (email, custom fields, lists, tags), usePUT(see Step 5).- Both
primaryandsecondaryare required when transitioning a contact to Inactive. - Inactive contacts cannot be reactivated via the API. See About contact status above.
5. Replace a contact (full update)
UsePUT to update any field on a contact: email, custom fields, lists, tags, or status. Unlike PATCH, PUT accepts the full contact body.
Important: PUT replaces the entire contact. Any field you omit from the request body will be removed from the contact’s stored data. To avoid accidentally losing data, follow these three steps:
- (If needed) Find the contact’s
_idvia Search Contacts. - Send a
GETrequest to retrieve the contact’s full data, including its current__vversion. - Send a
PUTrequest using the GET response as your starting body. Change only the fields you want to update, and include__vexactly as you received it from the GET.
200 OK):
__v:
__vis required on every PUT request. It’s the contact’s optimistic-concurrency version, which prevents one update from silently overwriting another.- Always use the
__vfrom a fresh GET — not a value cached from earlier. Successful PATCH or PUT calls increment__v, so a stale value will be rejected. - If
__vdoes not match the contact’s current version, the API returns a400 ConcurrencyError. Re-run the GET, rebuild your PUT body, and retry.
- Status follows the same rules as PATCH: capitalized values, both
primaryandsecondaryrequired for Inactive. - For an Active contact, send
"status": { "primary": "Active" }(nosecondary). - PUT cannot change an Inactive contact back to Active — see About contact status.
6. Delete a contact
200 OK): Returns the deleted contact object.
7. List all contacts
Retrieve all contacts for your account. This endpoint returns all matching contacts in a single response (no pagination). For large contact databases, use Search Contacts instead, which supports pagination and filtering.200 OK):
Common errors
Next steps
- Search Contacts — find contacts by email or custom field values
- Manage Lists — organize contacts into lists
- Manage Custom Fields — define the fields available on your contacts
- Migration from Legacy — end-to-end workflow for importing your data