Docs
guides · contacts

Contacts

A lightweight address book scoped to each project — store the people you email, attach your own metadata, and track their subscribe state. It's a record-keeping layer, not a marketing list.

Contacts let you keep names, custom metadata, and subscribe status alongside an email address. Each project has its own address book. Drin stays transactional — there are no broadcasts or campaigns here — but a contact record is a convenient home for the data your own send logic reads.

Contacts vs suppressionsA contact's subscribed flag is your opt-in state — you decide what it means and whether to honour it before sending. Suppressions are enforced by Drin at send time and populated automatically by bounces and complaints. They're separate systems; unsubscribing a contact does not suppress the address, and vice versa.

01 Create a contact

POST/v1/contacts

Only email is required. firstName, lastName, subscribed (defaults to true), and an arbitrary metadata object are optional.

curl https://api.drin.run/v1/contacts \
  -H "Authorization: Bearer $DRIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "sam@example.com",
    "firstName": "Sam",
    "lastName": "Rivera",
    "metadata": { "plan": "pro" }
  }'
201 Contact
{
  "id": "ct_0K3a…",
  "email": "sam@example.com",
  "firstName": "Sam",
  "lastName": "Rivera",
  "subscribed": true,
  "unsubscribedAt": null,
  "metadata": { "plan": "pro" },
  "createdAt": "2026-06-02T09:00:00Z",
  "updatedAt": "2026-06-02T09:00:00Z"
}

02 List & search

GET/v1/contacts

Cursor-paged like every list endpoint. Filter with subscribed=true|false and search names and email with q.

# All contacts
curl "https://api.drin.run/v1/contacts?limit=50" \
  -H "Authorization: Bearer $DRIN_API_KEY"

# Only subscribed, matching a search term
curl "https://api.drin.run/v1/contacts?subscribed=true&q=rivera" \
  -H "Authorization: Bearer $DRIN_API_KEY"

03 Update

PATCH/v1/contacts/{id}

Partial update — send only the fields you want to change. firstName and lastName accept null to clear them. To change subscribe state, use the dedicated endpoints below rather than patching it directly.

curl -X PATCH https://api.drin.run/v1/contacts/ct_0K3a \
  -H "Authorization: Bearer $DRIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "lastName": null, "metadata": { "plan": "scale" } }'

04 Unsubscribe & resubscribe

POST/v1/contacts/{id}/unsubscribe

Unsubscribing flips subscribed to false and stamps unsubscribedAt. Resubscribing flips it back and clears the timestamp. Both return the updated contact.

# Unsubscribe (stamps unsubscribedAt, flips subscribed → false)
curl -X POST https://api.drin.run/v1/contacts/ct_0K3a/unsubscribe \
  -H "Authorization: Bearer $DRIN_API_KEY"

# Resubscribe (clears unsubscribedAt, flips subscribed → true)
curl -X POST https://api.drin.run/v1/contacts/ct_0K3a/resubscribe \
  -H "Authorization: Bearer $DRIN_API_KEY"
Honouring opt-outThese endpoints record intent — they don't block sending on their own. Check subscribed in your own send path, or add the address to suppressions when you want Drin to enforce the opt-out at the gateway.

05 Delete

DELETE/v1/contacts/{id}

Permanently removes the contact record. Returns 204 No Content. This deletes the address-book entry only — it has no effect on already-sent messages or the suppression list.

cURL
curl -X DELETE https://api.drin.run/v1/contacts/ct_0K3a \
  -H "Authorization: Bearer $DRIN_API_KEY"

06 Related