Reference
Contacts
People you might message. Idempotent on email within a workspace; bulk endpoints exist for high-volume integrations.
List contact import jobs
/v1/contact_imports curl -X GET "https://api.clement.so/v1/contact_imports" \
-H "Authorization: Bearer $CLEMENT_API_KEY" Query parameters
-
limitinteger -
starting_afterstring -
ending_beforestring
Response200
OK
| Property | Type | Description |
|---|---|---|
object
required
| enum (1) | "list" |
data
required
| array<ContactImport> | |
has_more
required
| boolean | |
next_cursor
required
| string | null | |
prev_cursor
required
| string | null |
Example response
{
"object": "list",
"data": [
{
"id": "imp_01HZ8X3J9K7P2QV6R5N4M3T2LE",
"object": "contact_import",
"mode": "live",
"status": "succeeded",
"total_rows": 1850,
"succeeded_rows": 1842,
"failed_rows": 8,
"errors": [
{
"row": 17,
"email": "not-an-email",
"error": {
"type": "invalid_request_error",
"code": "invalid_email",
"message": "Email is missing or malformed"
}
}
],
"audience_id": "aud_01HZ8X3J9K7P2QV6R5N4M3T2L3",
"audience_created": false,
"created_at": "2026-05-02T16:00:00Z",
"started_at": "2026-05-02T16:00:01Z",
"completed_at": "2026-05-02T16:00:43Z"
}
],
"has_more": false,
"next_cursor": "string",
"prev_cursor": "string"
} Errors
-
400Invalid request -
401Authentication failed -
403Insufficient scope -
404Not found -
409Conflict -
422Validation failed -
429Rate limited
See Errors for the full envelope and per-code reference.
Bulk-import contacts from a CSV
/v1/contact_imports v1 processes synchronously; the response is the completed (or failed) import job. Larger files (>50k rows) will move to async polling once worker support lands; the resource shape is forward-compatible with `status: queued`. `?dry_run=true` parses + validates the CSV without writing any contacts, audience rows, or import_jobs row. Useful for agents that want to surface row-level errors to a human before committing.
curl -X POST "https://api.clement.so/v1/contact_imports" \
-H "Authorization: Bearer $CLEMENT_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" Query parameters
-
dry_runstring
Response200
Dry-run result (only when `?dry_run=true`)
| Property | Type | Description | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
id
required
| string |
Example: "dryrun_a1b2c3d4" | ||||||||||||||||||||||||
object
required
| enum (1) | "contact_import_dry_run" | ||||||||||||||||||||||||
mode
required
| enum (2) | "live""test" | ||||||||||||||||||||||||
total_rows
required
| integer | |||||||||||||||||||||||||
valid_rows
required
| integer | Rows that would be upserted on a real run. Duplicates within the file are excluded; existing-email upserts are still counted (they'd no-op). | ||||||||||||||||||||||||
invalid_rows
required
| integer | |||||||||||||||||||||||||
errors
required
| array<object> | Show properties
|
Example response
{
"id": "dryrun_a1b2c3d4",
"object": "contact_import_dry_run",
"mode": "live",
"total_rows": 0,
"valid_rows": 0,
"invalid_rows": 0,
"errors": [
{
"row": 0,
"email": "alice@example.com",
"error": {
"type": "string",
"code": "string",
"message": "string"
}
}
]
} Errors
-
400Invalid request -
401Authentication failed -
403Insufficient scope -
404Not found -
409Conflict -
422Validation failed -
429Rate limited
See Errors for the full envelope and per-code reference.
Retrieve a contact import job
/v1/contact_imports/{id} curl -X GET "https://api.clement.so/v1/contact_imports/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1" \
-H "Authorization: Bearer $CLEMENT_API_KEY" Path parameters
-
idstring required
Response200
OK
| Property | Type | Description | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
id
required
| string | |||||||||||||||||||||||||
object
required
| enum (1) | "contact_import" | ||||||||||||||||||||||||
mode
required
| enum (2) | "live""test" | ||||||||||||||||||||||||
status
required
| enum (4) | "queued""processing""succeeded""failed" | ||||||||||||||||||||||||
total_rows
required
| integer | |||||||||||||||||||||||||
succeeded_rows
required
| integer | |||||||||||||||||||||||||
failed_rows
required
| integer | |||||||||||||||||||||||||
errors
required
| array<object> | Show properties
| ||||||||||||||||||||||||
audience_id
required
| string | null | |||||||||||||||||||||||||
audience_created
required
| boolean | |||||||||||||||||||||||||
created_at
required
| string (date-time) | |||||||||||||||||||||||||
started_at
required
| string (date-time) | null | |||||||||||||||||||||||||
completed_at
required
| string (date-time) | null |
Example response
{
"id": "imp_01HZ8X3J9K7P2QV6R5N4M3T2LE",
"object": "contact_import",
"mode": "live",
"status": "succeeded",
"total_rows": 1850,
"succeeded_rows": 1842,
"failed_rows": 8,
"errors": [
{
"row": 17,
"email": "not-an-email",
"error": {
"type": "invalid_request_error",
"code": "invalid_email",
"message": "Email is missing or malformed"
}
}
],
"audience_id": "aud_01HZ8X3J9K7P2QV6R5N4M3T2L3",
"audience_created": false,
"created_at": "2026-05-02T16:00:00Z",
"started_at": "2026-05-02T16:00:01Z",
"completed_at": "2026-05-02T16:00:43Z"
} Errors
-
400Invalid request -
401Authentication failed -
403Insufficient scope -
404Not found -
409Conflict -
422Validation failed -
429Rate limited
See Errors for the full envelope and per-code reference.
List contacts
/v1/contacts curl -X GET "https://api.clement.so/v1/contacts" \
-H "Authorization: Bearer $CLEMENT_API_KEY" Query parameters
-
limitinteger -
starting_afterstring -
ending_beforestring -
emailstring -
qstring -
global_statusstring -
lead_statusstring
Response200
OK
| Property | Type | Description |
|---|---|---|
object
required
| enum (1) | "list" |
data
required
| array<Contact> | |
has_more
required
| boolean | |
next_cursor
required
| string | null | |
prev_cursor
required
| string | null |
Example response
{
"object": "list",
"data": [
{
"id": "con_01HZ8X3J9K7P2QV6R5N4M3T2L2",
"object": "contact",
"mode": "live",
"email": "alice@example.com",
"attributes": {
"first_name": "Alice",
"company": "Acme",
"title": "VP Eng"
},
"global_status": "ok",
"lead_status": null,
"created_at": "2026-04-20T10:14:00Z",
"updated_at": "2026-04-20T10:14:00Z"
}
],
"has_more": false,
"next_cursor": "string",
"prev_cursor": "string"
} Errors
-
400Invalid request -
401Authentication failed -
403Insufficient scope -
404Not found -
409Conflict -
422Validation failed -
429Rate limited
See Errors for the full envelope and per-code reference.
Create a contact (idempotent on email)
/v1/contacts Returns the existing contact (HTTP 200) when the email is already in this workspace. Otherwise creates and returns the new contact (HTTP 201).
curl -X POST "https://api.clement.so/v1/contacts" \
-H "Authorization: Bearer $CLEMENT_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"email": "alice@example.com",
"attributes": {},
"global_status": "ok"
}' Request body
| Property | Type | Description |
|---|---|---|
email
required
| string (email) | |
attributes | object | |
global_status | enum (4) | "ok""unsubscribed""bounced""blacklisted" |
Response200
Existing contact returned
| Property | Type | Description |
|---|---|---|
id
required
| string | |
object
required
| enum (1) | "contact" |
mode
required
| enum (2) | "live""test" |
email
required
| string (email) | |
attributes
required
| object | |
global_status
required
| enum (4) | "ok""unsubscribed""bounced""blacklisted" |
lead_status
required
| enum (3) | null | Read-only here; set with POST /v1/contacts/{id}/lead_status. The workflow tag a member gave the lead after a reply. Stored as `attributes.lead_classification`. "interested""meeting_booked""not_interested" |
created_at
required
| string (date-time) | |
updated_at
required
| string (date-time) |
Example response
{
"id": "con_01HZ8X3J9K7P2QV6R5N4M3T2L2",
"object": "contact",
"mode": "live",
"email": "alice@example.com",
"attributes": {
"first_name": "Alice",
"company": "Acme",
"title": "VP Eng"
},
"global_status": "ok",
"lead_status": null,
"created_at": "2026-04-20T10:14:00Z",
"updated_at": "2026-04-20T10:14:00Z"
} Errors
-
400Invalid request -
401Authentication failed -
403Insufficient scope -
404Not found -
409Conflict -
422Validation failed -
429Rate limited
See Errors for the full envelope and per-code reference.
Create up to 1,000 contacts in one call
/v1/contacts/batch Per-row idempotency on email — existing contacts are returned alongside newly-created ones. Validation failures are reported per-row in `errors`; partial success is the norm.
curl -X POST "https://api.clement.so/v1/contacts/batch" \
-H "Authorization: Bearer $CLEMENT_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"contacts": [
{
"email": "alice@example.com",
"attributes": {},
"global_status": "ok"
}
]
}' Request body
| Property | Type | Description |
|---|---|---|
contacts
required
| array<ContactCreate> |
Response200
Result
| Property | Type | Description | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
object
required
| enum (1) | "batch_result" | ||||||||||||||||||||||||
created
required
| array<Contact> | |||||||||||||||||||||||||
errors
required
| array<object> | Show properties
|
Example response
{
"object": "batch_result",
"created": [
{
"id": "con_01HZ8X3J9K7P2QV6R5N4M3T2L2",
"object": "contact",
"mode": "live",
"email": "alice@example.com",
"attributes": {
"first_name": "Alice",
"company": "Acme",
"title": "VP Eng"
},
"global_status": "ok",
"lead_status": null,
"created_at": "2026-04-20T10:14:00Z",
"updated_at": "2026-04-20T10:14:00Z"
}
],
"errors": [
{
"index": 0,
"email": "alice@example.com",
"error": {
"type": "string",
"code": "string",
"message": "string"
}
}
]
} Errors
-
400Invalid request -
401Authentication failed -
403Insufficient scope -
404Not found -
409Conflict -
422Validation failed -
429Rate limited
See Errors for the full envelope and per-code reference.
Retrieve a contact
/v1/contacts/{id} curl -X GET "https://api.clement.so/v1/contacts/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1" \
-H "Authorization: Bearer $CLEMENT_API_KEY" Path parameters
-
idstring required
Response200
OK
| Property | Type | Description |
|---|---|---|
id
required
| string | |
object
required
| enum (1) | "contact" |
mode
required
| enum (2) | "live""test" |
email
required
| string (email) | |
attributes
required
| object | |
global_status
required
| enum (4) | "ok""unsubscribed""bounced""blacklisted" |
lead_status
required
| enum (3) | null | Read-only here; set with POST /v1/contacts/{id}/lead_status. The workflow tag a member gave the lead after a reply. Stored as `attributes.lead_classification`. "interested""meeting_booked""not_interested" |
created_at
required
| string (date-time) | |
updated_at
required
| string (date-time) |
Example response
{
"id": "con_01HZ8X3J9K7P2QV6R5N4M3T2L2",
"object": "contact",
"mode": "live",
"email": "alice@example.com",
"attributes": {
"first_name": "Alice",
"company": "Acme",
"title": "VP Eng"
},
"global_status": "ok",
"lead_status": null,
"created_at": "2026-04-20T10:14:00Z",
"updated_at": "2026-04-20T10:14:00Z"
} Errors
-
400Invalid request -
401Authentication failed -
403Insufficient scope -
404Not found -
409Conflict -
422Validation failed -
429Rate limited
See Errors for the full envelope and per-code reference.
Update a contact
/v1/contacts/{id} curl -X PATCH "https://api.clement.so/v1/contacts/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1" \
-H "Authorization: Bearer $CLEMENT_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"attributes": {},
"global_status": "ok"
}' Path parameters
-
idstring required
Request body
| Property | Type | Description |
|---|---|---|
attributes | object | Merged into existing attributes. Set a key to `null` to remove it from the contact. |
global_status | enum (4) | "ok""unsubscribed""bounced""blacklisted" |
Response200
Updated
| Property | Type | Description |
|---|---|---|
id
required
| string | |
object
required
| enum (1) | "contact" |
mode
required
| enum (2) | "live""test" |
email
required
| string (email) | |
attributes
required
| object | |
global_status
required
| enum (4) | "ok""unsubscribed""bounced""blacklisted" |
lead_status
required
| enum (3) | null | Read-only here; set with POST /v1/contacts/{id}/lead_status. The workflow tag a member gave the lead after a reply. Stored as `attributes.lead_classification`. "interested""meeting_booked""not_interested" |
created_at
required
| string (date-time) | |
updated_at
required
| string (date-time) |
Example response
{
"id": "con_01HZ8X3J9K7P2QV6R5N4M3T2L2",
"object": "contact",
"mode": "live",
"email": "alice@example.com",
"attributes": {
"first_name": "Alice",
"company": "Acme",
"title": "VP Eng"
},
"global_status": "ok",
"lead_status": null,
"created_at": "2026-04-20T10:14:00Z",
"updated_at": "2026-04-20T10:14:00Z"
} Errors
-
400Invalid request -
401Authentication failed -
403Insufficient scope -
404Not found -
409Conflict -
422Validation failed -
429Rate limited
See Errors for the full envelope and per-code reference.
Delete a contact
/v1/contacts/{id} Hard delete. Cascades to `campaign_contacts`; historical `sends` keep their email snapshot.
curl -X DELETE "https://api.clement.so/v1/contacts/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1" \
-H "Authorization: Bearer $CLEMENT_API_KEY" Path parameters
-
idstring required
Response200
Deleted
| Property | Type | Description |
|---|---|---|
id
required
| string | |
object
required
| enum (1) | "contact" |
mode
required
| enum (2) | "live""test" |
email
required
| string (email) | |
attributes
required
| object | |
global_status
required
| enum (4) | "ok""unsubscribed""bounced""blacklisted" |
lead_status
required
| enum (3) | null | Read-only here; set with POST /v1/contacts/{id}/lead_status. The workflow tag a member gave the lead after a reply. Stored as `attributes.lead_classification`. "interested""meeting_booked""not_interested" |
created_at
required
| string (date-time) | |
updated_at
required
| string (date-time) |
Example response
{
"id": "con_01HZ8X3J9K7P2QV6R5N4M3T2L2",
"object": "contact",
"mode": "live",
"email": "alice@example.com",
"attributes": {
"first_name": "Alice",
"company": "Acme",
"title": "VP Eng"
},
"global_status": "ok",
"lead_status": null,
"created_at": "2026-04-20T10:14:00Z",
"updated_at": "2026-04-20T10:14:00Z"
} Errors
-
400Invalid request -
401Authentication failed -
403Insufficient scope -
404Not found -
409Conflict -
422Validation failed -
429Rate limited
See Errors for the full envelope and per-code reference.
Set a contact's lead status
/v1/contacts/{id}/lead_status Tags the lead `interested`, `meeting_booked` or `not_interested`, as the dashboard's inbox does after a reply. `not_interested` also stops the sequence: the contact's active or replied enrollments (only the one in `campaign_id` when given) become `unsubscribed`. Emits `contact.lead_status_changed` when the status changes.
curl -X POST "https://api.clement.so/v1/contacts/obj_01HZ8X3J9K7P2QV6R5N4M3T2L1/lead_status" \
-H "Authorization: Bearer $CLEMENT_API_KEY" \
-H "Content-Type: application/json" \
--data '{
"lead_status": "interested",
"campaign_id": "cmp_01HZ8X3J9K7P2QV6R5N4M3T2L4"
}' Path parameters
-
idstring required
Request body
| Property | Type | Description |
|---|---|---|
lead_status
required
| enum (3) | "interested""meeting_booked""not_interested" |
campaign_id | string | The campaign the reply came from: limits `not_interested` to that enrollment and is recorded on the event. |
Response200
Updated
| Property | Type | Description |
|---|---|---|
id
required
| string | |
object
required
| enum (1) | "contact" |
mode
required
| enum (2) | "live""test" |
email
required
| string (email) | |
attributes
required
| object | |
global_status
required
| enum (4) | "ok""unsubscribed""bounced""blacklisted" |
lead_status
required
| enum (3) | null | Read-only here; set with POST /v1/contacts/{id}/lead_status. The workflow tag a member gave the lead after a reply. Stored as `attributes.lead_classification`. "interested""meeting_booked""not_interested" |
created_at
required
| string (date-time) | |
updated_at
required
| string (date-time) |
Example response
{
"id": "con_01HZ8X3J9K7P2QV6R5N4M3T2L2",
"object": "contact",
"mode": "live",
"email": "alice@example.com",
"attributes": {
"first_name": "Alice",
"company": "Acme",
"title": "VP Eng"
},
"global_status": "ok",
"lead_status": null,
"created_at": "2026-04-20T10:14:00Z",
"updated_at": "2026-04-20T10:14:00Z"
} Errors
-
400Invalid request -
401Authentication failed -
403Insufficient scope -
404Not found -
409Conflict -
422Validation failed -
429Rate limited
See Errors for the full envelope and per-code reference.