Contacts
Create, list, update, and delete contacts. Retrieve a contact's sends, scheduled sends, sequence enrolments, segment memberships, and timeline.
Contacts represent the people in your audience. Each contact has a unique email address and a set of custom properties you can use for personalisation and segmentation.
Create contact
POST /api/contactsCreates a new contact. The email address must be unique within your organisation.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | The contact's email address. |
userId | string | No | Your own external identifier for the contact. |
properties | object | No | Key-value pairs of custom properties (e.g. firstName, plan). |
Response
Returns the created contact with a generated contactId. The derived object holds system-computed engagement fields, and subscribed/suppressed track consent and deliverability state.
{
"contactId": "cnt_01H8MZXK...",
"email": "alice@example.com",
"properties": { "firstName": "Alice", "plan": "pro" },
"derived": {
"totalSends": 0,
"totalOpens": 0,
"totalClicks": 0,
"lastEventAt": null
},
"subscribed": true,
"suppressed": false,
"createdAt": "2025-09-15T10:30:00.000Z",
"updatedAt": "2025-09-15T10:30:00.000Z"
}To create-or-update in a single call, use PUT /api/contacts (upsert by email); it returns the contact with an extra created boolean.
Errors
| Code | Description |
|---|---|
VALIDATION_ERROR | Missing or invalid email address. |
CONTACT_EXISTS | A contact with this email already exists. |
Examples
curl -X POST https://api.kraiter.com/api/contacts \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"email": "alice@example.com",
"properties": { "firstName": "Alice", "plan": "pro" }
}'const contact = await kraiter.contacts.create({
email: "alice@example.com",
properties: { firstName: "Alice", plan: "pro" },
});List contacts
GET /api/contactsReturns a paginated list of contacts. Passing an email query parameter instead performs an exact lookup and returns that single contact object (not a list).
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
cursor | string | — | Pagination cursor from a previous response. |
limit | number | 20 | Number of contacts to return (max 100). |
email | string | — | Exact email lookup. When provided, returns the single matching contact (or CONTACT_NOT_FOUND) rather than a paginated list. |
Response
{
"items": [
{
"contactId": "cnt_01H8MZXK...",
"email": "alice@example.com",
"properties": { "firstName": "Alice" },
"subscribed": true,
"suppressed": false,
"createdAt": "2025-09-15T10:30:00.000Z",
"updatedAt": "2025-09-15T10:30:00.000Z"
}
],
"nextCursor": "eyJpZCI6ImNudF8wMUgi..."
}Examples
curl "https://api.kraiter.com/api/contacts?limit=10" \
-H "Authorization: Bearer YOUR_API_KEY"
# Exact email lookup — returns a single contact object
curl "https://api.kraiter.com/api/contacts?email=alice@example.com" \
-H "Authorization: Bearer YOUR_API_KEY"const page = await kraiter.contacts.list({ limit: 10 });Contact statistics
GET /api/contacts/statsReturns tenant-wide contact counts. Useful for dashboards and headline figures.
Response
{
"total": 1240,
"suppressedCount": 18,
"addedLast24h": 12,
"addedLast7d": 86
}| Field | Type | Description |
|---|---|---|
total | number | Total contacts for the organisation. |
suppressedCount | number | Contacts currently suppressed (bounce or complaint). |
addedLast24h | number | Contacts created in the last 24 hours. |
addedLast7d | number | Contacts created in the last 7 days. |
Examples
curl https://api.kraiter.com/api/contacts/stats \
-H "Authorization: Bearer YOUR_API_KEY"Get contact
GET /api/contacts/:idReturns a single contact by ID.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The contact ID. |
Response
Returns the full contact object.
Errors
| Code | Description |
|---|---|
CONTACT_NOT_FOUND | No contact with this ID exists. |
Examples
curl https://api.kraiter.com/api/contacts/cnt_01H8MZXK... \
-H "Authorization: Bearer YOUR_API_KEY"const contact = await kraiter.contacts.get("cnt_01H8MZXK...");Update contact
PATCH /api/contacts/:idUpdates a contact's userId, properties, or subscription state. Only the fields you include are changed — omitted fields remain unchanged. At least one field must be provided. A contact's email address cannot be changed via this endpoint.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The contact ID. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
userId | string | No | New external identifier for the contact. |
properties | object | No | Properties to merge with existing properties. |
subscribed | boolean | No | Set the contact's marketing subscription state. |
Response
Returns the updated contact.
Errors
| Code | Description |
|---|---|
CONTACT_NOT_FOUND | No contact with this ID exists. |
VALIDATION_ERROR | No fields were provided to update. |
Examples
curl -X PATCH https://api.kraiter.com/api/contacts/cnt_01H8MZXK... \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "properties": { "plan": "enterprise" } }'const contact = await kraiter.contacts.update("cnt_01H8MZXK...", {
properties: { plan: "enterprise" },
});Delete contact
DELETE /api/contacts/:idPermanently deletes a contact. This also removes any sequence enrolments and scheduled sends for the contact.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The contact ID. |
Response
Returns 204 No Content on success.
Errors
| Code | Description |
|---|---|
CONTACT_NOT_FOUND | No contact with this ID exists. |
Examples
curl -X DELETE https://api.kraiter.com/api/contacts/cnt_01H8MZXK... \
-H "Authorization: Bearer YOUR_API_KEY"await kraiter.contacts.delete("cnt_01H8MZXK...");List sends for contact
GET /api/contacts/:id/sendsReturns all emails sent to a specific contact.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The contact ID. |
Response
{
"items": [
{
"sendId": "snd_01H9...",
"templateId": "welcome-email",
"deliveryStatus": "delivered",
"sentAt": "2025-09-15T11:00:00.000Z"
}
],
"nextCursor": null
}The list also accepts templateId and sequenceId query parameters to filter the contact's sends.
Examples
curl "https://api.kraiter.com/api/contacts/cnt_01H8MZXK.../sends" \
-H "Authorization: Bearer YOUR_API_KEY"const sends = await kraiter.contacts.listSends("cnt_01H8MZXK...");List scheduled sends for contact
GET /api/contacts/:id/scheduledReturns pending scheduled sends for a contact.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The contact ID. |
Response
Returns a paginated list of scheduled send objects.
Examples
curl "https://api.kraiter.com/api/contacts/cnt_01H8MZXK.../scheduled" \
-H "Authorization: Bearer YOUR_API_KEY"const scheduled = await kraiter.contacts.listScheduled("cnt_01H8MZXK...");List sequence enrolments for contact
GET /api/contacts/:id/sequencesReturns the sequences a contact is currently enrolled in or has previously completed.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The contact ID. |
Response
This endpoint returns the full set of enrolments in a single items array (it is not cursor-paginated).
{
"items": [
{
"sequenceId": "onboarding",
"status": "active",
"enrolledAt": "2025-09-15T10:30:00.000Z",
"currentStep": 2
}
]
}Examples
curl "https://api.kraiter.com/api/contacts/cnt_01H8MZXK.../sequences" \
-H "Authorization: Bearer YOUR_API_KEY"const enrolments = await kraiter.contacts.listSequences("cnt_01H8MZXK...");List segment memberships for contact
GET /api/contacts/:id/segmentsReturns the segments a contact currently belongs to.
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The contact ID. |
Response
This endpoint returns all current memberships in a single items array (it is not cursor-paginated). Each entry is enriched with the segment's name and cached memberCount.
{
"items": [
{
"segmentId": "seg_01H9...",
"name": "Active Users",
"memberCount": 342,
"computedAt": "2025-09-15T10:30:00.000Z"
}
]
}Examples
curl "https://api.kraiter.com/api/contacts/cnt_01H8MZXK.../segments" \
-H "Authorization: Bearer YOUR_API_KEY"const segments = await kraiter.contacts.listSegments("cnt_01H8MZXK...");Get contact timeline
GET /api/contacts/:id/timelineReturns a unified timeline of events, sends, delivery/engagement, sequence transitions, and scheduled sends for a contact, sorted in reverse chronological order (most recent first).
Path parameters
| Parameter | Type | Description |
|---|---|---|
id | string | The contact ID. |
Query parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 50 | Maximum number of entries to return (max 100). |
from | string | — | Only include entries at or after this ISO 8601 timestamp. |
to | string | — | Only include entries at or before this ISO 8601 timestamp. |
Response
Each entry has a type, a timestamp, and a data object whose shape depends on the type. The response uses hasMore (a boolean) rather than a cursor.
{
"items": [
{
"type": "delivered",
"timestamp": "2025-09-15T11:00:00.000Z",
"data": {
"sendId": "snd_01H9...",
"templateId": "welcome-email"
}
},
{
"type": "event",
"timestamp": "2025-09-15T10:45:00.000Z",
"data": {
"eventId": "evt_01H9...",
"name": "page_viewed",
"properties": { "url": "/pricing" }
}
}
],
"hasMore": false
}Entry type values include event, send, delivered, bounced, complained, opened, clicked, sequence_entered, sequence_exited, sequence_completed, and scheduled.
Examples
curl "https://api.kraiter.com/api/contacts/cnt_01H8MZXK.../timeline?from=2025-09-01T00:00:00.000Z" \
-H "Authorization: Bearer YOUR_API_KEY"const timeline = await kraiter.contacts.getTimeline("cnt_01H8MZXK...");