Appearance
Update a contact
Change a contact's details, properties or subscription state.
http
PATCH /v1/contacts/{id}Requires a Full access API key.
Path parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | string | Yes | The contact id, or the contact's email address. |
Body parameters
Every field is optional — send only what you're changing. A field you leave out is left alone.
| Parameter | Type | Required | Description |
|---|---|---|---|
email | string | No | A new address. Max 254 characters, lower-cased on save, still unique within your team. |
first_name | string | null | No | Max 120 characters. null clears it. |
last_name | string | null | No | Max 120 characters. null clears it. |
unsubscribed | boolean | No | true unsubscribes the contact globally from all broadcasts and stamps unsubscribed_at. false resubscribes them and clears it. |
properties | object | No | A map of property key → value. Only the keys you send are touched; unknown keys are ignored. null or "" clears a value. |
Example
bash
curl -X PATCH "https://api.sendgrail.com/v1/contacts/ada@example.com" \
-H "Authorization: Bearer $SENDGRAIL_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"first_name": "Ada",
"properties": { "seats": 24 }
}'js
const res = await fetch(
`https://api.sendgrail.com/v1/contacts/${encodeURIComponent('ada@example.com')}`,
{
method: 'PATCH',
headers: {
Authorization: `Bearer ${process.env.SENDGRAIL_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
first_name: 'Ada',
properties: { seats: 24 },
}),
},
);
const contact = await res.json();Response
The body carries just the contact's id — fetch the full contact object with retrieve if you need the updated record back:
json
{
"object": "contact",
"id": "3f1c9a0e-6c5e-4a2b-9f2d-2b1a7c8e4d31"
}A contact.updated webhook event fires on success.
Properties merge, they don't replace
Sending { "properties": { "seats": 24 } } sets seats and leaves every other property as it was. There is no way to wipe all properties in one call — clear them individually by sending null.
The properties in the response is the resolved map: every property defined on your team, with its fallback applied where the contact has no value of its own. So a key you just cleared reappears with its fallback rather than disappearing. See The contact object.
A value for a number-typed property must be numeric or the whole request is a 422 — nothing is saved.
Unsubscribing
unsubscribed: true is the global opt-out: it sets unsubscribed_at to now, and the contact stops receiving broadcasts entirely, regardless of topics. Setting it back to false clears unsubscribed_at.
This is deliberately blunt. To opt someone out of one category only, use Contact topics instead.
Changing the address
Changing email moves the contact's identity — the address is what uniquely identifies them within your team. If the new address already belongs to another contact, the request is a 422; SendGrail will not merge two contacts for you, because there is no correct answer to which one's properties should win.
See Errors.