Upsert a contact
POST/contacts
POST /contacts: create or update a contact by phone, including custom fields, segments and opt-out.
Register or update a contact - the endpoint for Make, n8n, WordPress and other server-to-server automations. It is idempotent on the phone number: the same phone always resolves to the same contact, so a retried request is safe. A new contact returns 201 with created: true; an existing one returns 200 with created: false.
Fields merge - only the fields you send (with a non-empty value) are updated; omitted fields are left as they are, so one integration never wipes another's data. A new contact must be attached to at least one segment id (see List segments); on an existing contact, segments are added, never removed.
curl -X POST https://sendy.co.il/api/v1/contacts \
-H "Authorization: Bearer sk_live_8Jc…" \
-H "Content-Type: application/json" \
-d '{
"phone": "+972501234567",
"first_name": "Dana",
"last_name": "Levi",
"email": "dana@example.com",
"birthday": "1990-05-01",
"custom_field_1": "Tel Aviv",
"segments": ["cmc0seg0001"]
}'const res = await fetch(`https://sendy.co.il/api/v1/contacts`, {
method: "POST",
headers: {
Authorization: "Bearer sk_live_8Jc…",
"Content-Type": "application/json",
},
body: JSON.stringify({
phone: "+972501234567",
first_name: "Dana",
last_name: "Levi",
email: "dana@example.com",
birthday: "1990-05-01",
custom_field_1: "Tel Aviv",
segments: ["cmc0seg0001"],
}),
});
const contact = await res.json();{
"id": "cmc0contact0001",
"phone": "+972501234567",
"first_name": "Dana",
"last_name": "Levi",
"email": "dana@example.com",
"birthday": "1990-05-01",
"custom_field_1": "Tel Aviv",
"custom_field_2": null,
"custom_field_3": null,
"subscribed": true,
"source": "MANUAL",
"segments": [{ "id": "cmc0seg0001", "name": "Newsletter" }],
"created_at": "2026-07-15T10:00:00.000Z",
"updated_at": "2026-07-15T10:00:00.000Z",
"created": true
}phone is an Israeli mobile (E.164 +9725XXXXXXXX or local 05XXXXXXXX). birthday is YYYY-MM-DD. custom_field_1-3 are your workspace's tenant-defined fields (≤200 chars) - call List custom fields to see what each slot means; unnamed slots are ignored. To clear a field or remove a segment, use the web app - v1 only merges.
Unsubscribing. Send "subscribed": false to record an opt-out you received elsewhere (your CRM, a form, a phone call). It works for an existing contact and for a phone that is not a contact yet - that one is created already unsubscribed, and the segments array is optional for it. The number is also added to your workspace blocklist, so it stays suppressed even if the contact is later deleted and re-imported, and a contact.opted_out webhook fires with reason: "api". Repeating the call is safe (no second event). The API never re-subscribes: omitting subscribed leaves the status as it is, and "subscribed": true on a contact who unsubscribed returns 400 VALIDATION (detail code RESUBSCRIBE_NOT_ALLOWED). Re-subscribing is done in the web app.
curl -X POST https://sendy.co.il/api/v1/contacts \
-H "Authorization: Bearer sk_live_8Jc…" \
-H "Content-Type: application/json" \
-d '{ "phone": "+972501234567", "subscribed": false }'