API reference

Contacts API

Reference for creating, listing, retrieving, updating and deleting contacts, and managing their segments and topics.

The contacts API manages the contacts of the project that owns your API key. Every endpoint needs a full_access key. For the concepts, see Contacts.

In every path, {identifier} is either the contact ID or the contact's email address.

The contact object

JSON
{
  "object": "contact",
  "id": "jx7r4k9m2c5v8n1b3q6w0e2t",
  "email": "ada@example.com",
  "first_name": "Ada",
  "last_name": "Lovelace",
  "unsubscribed": false,
  "properties": { "company": "Acme" },
  "created_at": "2026-11-14T10:30:00.000Z"
}

Create a contact

POST/contacts
FieldTypeRequiredDescription
emailstringYes3 to 320 characters. Stored in lowercase.
first_namestringNoUp to 200 characters.
last_namestringNoUp to 200 characters.
unsubscribedbooleanNoDefault false.
propertiesobjectNoValues must be strings, numbers or null. Each key must be a declared contact property.
segmentsobject[]No[{ "id": "..." }].
topicsobject[]No[{ "id": "...", "subscription": "opt_in" }]. subscription is opt_in or opt_out.
Shell
curl -X POST https://api.sooner.dev/contacts \
  -H "Authorization: Bearer $SOONER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "ada@example.com",
    "first_name": "Ada",
    "properties": { "company": "Acme" },
    "segments": [{ "id": "jd5f2h8k4m7p1r6t9v3x0z2c" }]
  }'
JSON
{ "object": "contact", "id": "jx7r4k9m2c5v8n1b3q6w0e2t" }

Notable errors: validation_error for an invalid email, a duplicate email, an undeclared property or one of the wrong type, or your plan's contact limit. not_found for an unknown segment or topic.

List contacts

GET/contacts

Returns the project's contacts, newest first. Accepts limit and after. See Pagination.

Shell
curl "https://api.sooner.dev/contacts?limit=2" \
  -H "Authorization: Bearer $SOONER_API_KEY"
JSON
{
  "object": "list",
  "data": [
    {
      "object": "contact",
      "id": "jx7r4k9m2c5v8n1b3q6w0e2t",
      "email": "ada@example.com",
      "first_name": "Ada",
      "last_name": "Lovelace",
      "unsubscribed": false,
      "properties": { "company": "Acme" },
      "created_at": "2026-11-14T10:30:00.000Z"
    }
  ],
  "has_more": false
}

Retrieve a contact

GET/contacts/{identifier}
Shell
curl https://api.sooner.dev/contacts/ada@example.com \
  -H "Authorization: Bearer $SOONER_API_KEY"

The response is the contact object. Notable errors: not_found.

Update a contact

PATCH/contacts/{identifier}

Changes only the fields you send. properties is merged into the existing values.

FieldTypeRequiredDescription
first_namestring or nullNonull clears it.
last_namestring or nullNonull clears it.
unsubscribedbooleanNoThe global opt-out.
propertiesobjectNoStrings, numbers or null. Keys must be declared.
Shell
curl -X PATCH https://api.sooner.dev/contacts/ada@example.com \
  -H "Authorization: Bearer $SOONER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "unsubscribed": true }'
JSON
{ "object": "contact", "id": "jx7r4k9m2c5v8n1b3q6w0e2t" }

Delete a contact

DELETE/contacts/{identifier}
Shell
curl -X DELETE https://api.sooner.dev/contacts/ada@example.com \
  -H "Authorization: Bearer $SOONER_API_KEY"
JSON
{
  "object": "contact",
  "contact": "jx7r4k9m2c5v8n1b3q6w0e2t",
  "id": "jx7r4k9m2c5v8n1b3q6w0e2t",
  "deleted": true
}

Add a contact to a segment

POST/contacts/{identifier}/segments/{segmentId}
Shell
curl -X POST https://api.sooner.dev/contacts/ada@example.com/segments/jd5f2h8k4m7p1r6t9v3x0z2c \
  -H "Authorization: Bearer $SOONER_API_KEY"
JSON
{ "id": "jd5f2h8k4m7p1r6t9v3x0z2c" }

Adding a contact that is already a member succeeds and changes nothing. Notable errors: not_found for an unknown contact or segment.

Remove a contact from a segment

DELETE/contacts/{identifier}/segments/{segmentId}

Removes the membership and keeps the contact.

JSON
{ "id": "jd5f2h8k4m7p1r6t9v3x0z2c", "deleted": true }

List a contact's segments

GET/contacts/{identifier}/segments

Returns every segment the contact is in. The list is not paginated.

JSON
{
  "object": "list",
  "data": [
    {
      "object": "segment",
      "id": "jd5f2h8k4m7p1r6t9v3x0z2c",
      "name": "Newsletter subscribers",
      "created_at": "2026-11-14T10:00:00.000Z"
    }
  ],
  "has_more": false
}

Update a contact's topics

PATCH/contacts/{identifier}/topics

Sets the contact's choice for one or more topics. The body is a JSON array.

FieldTypeRequiredDescription
idstringYesThe topic ID.
subscriptionstringYesopt_in or opt_out.
Shell
curl -X PATCH https://api.sooner.dev/contacts/ada@example.com/topics \
  -H "Authorization: Bearer $SOONER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[{ "id": "jt3b6n9q2w5e8r1y4u7i0o2p", "subscription": "opt_out" }]'
JSON
{ "id": "jx7r4k9m2c5v8n1b3q6w0e2t" }

Notable errors: not_found for an unknown contact or topic.

List a contact's topics

GET/contacts/{identifier}/topics

Returns every topic in the project with the contact's effective subscription, which is their own choice or the topic default.

JSON
{
  "object": "list",
  "data": [
    {
      "id": "jt3b6n9q2w5e8r1y4u7i0o2p",
      "name": "Product updates",
      "description": "New features and improvements",
      "subscription": "opt_out"
    }
  ],
  "has_more": false
}

Contacts in a segment

The same operations exist under a segment, for Resend compatibility: GET /segments/{id}/contacts, POST /segments/{id}/contacts, GET, PATCH and DELETE /segments/{id}/contacts/{identifier}. See the segments API reference.