PristineSend
Get started
API Reference

Contacts

Create, read, update, and delete contacts in your workspace. All endpoints require a Bearer token — see Authentication for details.

Overview

MethodPathDescription
GET/api/v1/contactsList contacts (paginated)
POST/api/v1/contactsCreate a contact
GET/api/v1/contacts/:idGet a single contact
PATCH/api/v1/contacts/:idUpdate a contact (partial)
DELETE/api/v1/contacts/:idDelete a contact

List contacts

GET/api/v1/contacts

Returns a cursor-paginated list of contacts for the authenticated workspace, newest first. The response includes has_more and next_cursor; to fetch the next page, pass next_cursor back as the cursor query parameter.

Pass segment_id to restrict the list to members of a segment (a UUID that must belong to your workspace). An unknown or foreign id returns 404 not_found, and a malformed (non-UUID) value returns 400 invalid_field. It combines with the other filters and with pagination.

Query parameters

FieldTypeRequiredDescription
limitnumberoptionalMax contacts to return. Default 50, max 100 (over-large values are clamped).
cursorstringoptionalOpaque pagination cursor from a previous response's next_cursor. Omit for the first page; a malformed cursor returns 400 invalid_field.
statusstringoptional'subscribed' or 'unsubscribed'. Omit for all.
searchstringoptionalFilter contacts whose email contains this substring.
segment_idstringoptionalFilter to members of a segment (UUID). The segment must belong to your workspace; an unknown or foreign id returns 404 not_found; a malformed (non-UUID) value returns 400 invalid_field.

Response

"color:#ff7b72">import { PristineSend } "color:#ff7b72">from "pristinesend"

"color:#ff7b72">const ps = "color:#ff7b72">new PristineSend(process.env.PRISTINESEND_API_KEY!)

// The returned page auto-paginates — iterate across every page transparently.
for "color:#ff7b72">await ("color:#ff7b72">const contact of "color:#ff7b72">await ps.contacts.list({ limit: 100, status: "subscribed" })) {
  console.log(contact.email)
}

Filter to one segment's members:

# Only contacts that belong to a segment
"color:#79c0ff">curl "https://pristinesend.com/api/v1/contacts?segment_id=8e4f2b1c-1a2b-4c3d-9e8f-0a1b2c3d4e5f" \
  "color:#ff7b72">-H "Authorization: Bearer ps_live_YOUR_API_KEY"

Create a contact

POST/api/v1/contacts

Creates a new contact. Returns 409 Conflict if the email already exists in the workspace.

Request body

FieldTypeRequiredDescription
emailstringrequiredContact email address. Must be unique per workspace.
first_namestringoptionalContact's first name.
last_namestringoptionalContact's last name.
statusstringoptional'subscribed' or 'unsubscribed'. Defaults to 'subscribed'.
propertiesobjectoptionalArbitrary key/value metadata. Must be a JSON object.

Response

"color:#ff7b72">import { PristineSend } "color:#ff7b72">from "pristinesend"

"color:#ff7b72">const ps = "color:#ff7b72">new PristineSend(process.env.PRISTINESEND_API_KEY!)

"color:#ff7b72">const contact = "color:#ff7b72">await ps.contacts.create({
  email: "jane@example.com",
  first_name: "Jane",
  last_name: "Doe",
  properties: { city: "Seattle" },
})
console.log("Created:", contact.id)

Get a contact

GET/api/v1/contacts/:id

Returns a single contact by UUID. Returns 404 if the contact doesn't exist or belongs to a different workspace.

"color:#ff7b72">import { PristineSend } "color:#ff7b72">from "pristinesend"

"color:#ff7b72">const ps = "color:#ff7b72">new PristineSend(process.env.PRISTINESEND_API_KEY!)

"color:#ff7b72">const contact = "color:#ff7b72">await ps.contacts.get("8e4f2b1c-1a2b-4c3d-9e8f-0a1b2c3d4e5f")
console.log(contact.email, contact.status)

Update a contact

PATCH/api/v1/contacts/:id

Partially updates a contact. Only include the fields you want to change. The properties field is merged with existing properties rather than replaced. Email cannot be changed after creation.

Request body

FieldTypeRequiredDescription
first_namestringoptionalNew first name.
last_namestringoptionalNew last name.
statusstringoptional'subscribed' or 'unsubscribed'.
propertiesobjectoptionalProperties to merge into existing properties. Email cannot be changed.

Response

"color:#ff7b72">import { PristineSend } "color:#ff7b72">from "pristinesend"

"color:#ff7b72">const ps = "color:#ff7b72">new PristineSend(process.env.PRISTINESEND_API_KEY!)

// properties is merged, not replaced; only include what changes.
"color:#ff7b72">const contact = "color:#ff7b72">await ps.contacts.update("8e4f2b1c-1a2b-4c3d-9e8f-0a1b2c3d4e5f", {
  status: "unsubscribed",
  properties: { tier: "pro" },
})
console.log(contact.status, contact.properties)

Delete a contact

DELETE/api/v1/contacts/:id

Permanently deletes a contact. Returns 204 No Content on success with no response body. Returns 404 if the contact doesn't exist.

"color:#ff7b72">import { PristineSend } "color:#ff7b72">from "pristinesend"

"color:#ff7b72">const ps = "color:#ff7b72">new PristineSend(process.env.PRISTINESEND_API_KEY!)

"color:#ff7b72">await ps.contacts.delete("8e4f2b1c-1a2b-4c3d-9e8f-0a1b2c3d4e5f")
// Resolves on 204 No Content; throws NotFoundError if the contact is gone.

Error codes

All errors return the standard envelope — a code, message, optional param (field-validation only), and a request_id (also the X-Request-Id header):

{
  "error": {
    "code": "invalid_field",
    "message": "Email is not a valid email address.",
    "param": "email",
    "request_id": "req_8f3c9a1b2d4e5f6071829304"
  }
}
StatuscodeWhen
400invalid_requestMalformed or unparseable JSON body
400missing_fieldA required field (e.g. email) is absent
400invalid_fieldA field is present but invalid, e.g. a bad email, a non-object properties, or a malformed cursor
401unauthorizedMissing or invalid API key
404not_foundContact doesn't exist in this workspace
409conflictEmail already exists in this workspace
500internal_errorUnexpected server error — safe to retry

See the full Error codes reference for general API errors.