API reference - Contacts
API reference · OpenAPI specification
Endpoint definitions below are generated from the API contract. Request and response blocks describe schemas, not example payloads. required lists mandatory fields; $ref refers to a linked model. Send JSON bodies with Content-Type: application/json unless the endpoint specifies binary content.
List or find accessible contacts
GET /v1/contacts
Requires contacts.read. Phone and case-insensitive email filters use exact AND matching against all saved channels, not only the primary channel. Multiple matches are returned; callers must not silently select one. Deleted and inaccessible contacts are excluded. The query filter searches normalized contact names, company, role and primary channels as a literal case-insensitive substring. Filters combine with AND and remain cursor-bound. Workspace contact access includes only workspace-visible contacts. Contacts restricted to shared or personal inboxes retain their audience and are excluded from Workspace contact sync. Personal-inbox connections follow the member's contact ownership and shares through their authorized personal inbox.
Parameters
| Name | Location | Required | Description and constraints |
|---|---|---|---|
limit | query | No | Type: integer default: 50 minimum: 1 maximum: 100 |
cursor | query | No | Opaque cursor from next_cursor. Reuse the same credentials, endpoint, filters, ordering, and limit. Results may change between requests; pagination does not freeze the dataset. Type: string |
created_after | query | No | Inclusive lower time bound. Applies to start_time for calls and created_at for contacts/messages. Include a time zone. Type: string format: "date-time" |
created_before | query | No | Exclusive upper time bound; must be later than created_after. Include a time zone. Type: string format: "date-time" |
phone | query | No | Type: string pattern: "^\+[1-9][0-9]{1,14}$" |
email | query | No | Type: string |
query | query | No | Literal substring of the contact search text; whitespace is trimmed. Wildcards are not interpreted. Type: string minLength: 3 maxLength: 100 |
order | query | No | oldest (the default) pages forward by ascending stable ID. newest returns the most recent records first and next_cursor continues with older records. The order is bound to the cursor. Type: string default: "oldest" enum: ["oldest","newest"] |
Responses
200 — Contacts in the requested order (ascending stable ID by default).
application/json
allOf:
- $ref: '#/components/schemas/Page'
- properties:
data:
type: array
items:
$ref: '#/components/schemas/Contact'
Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.
Error format, status codes, and retry guidance.
Create a contact
POST /v1/contacts
Requires contacts.read and contacts.write. Workspace connections create workspace contacts. Selected-inbox OAuth connections create contacts shared only with those inboxes. Single-inbox connections create contacts shared only with that inbox; personal-inbox contacts are owned by the subject. Clients cannot override the audience. Request ID deduplication lasts seven days. A nonnull valid E.164 phone is required; skip source records without a phone. Missing or null phone returns CONTACT_PHONE_REQUIRED.
Parameters
| Name | Location | Required | Description and constraints |
|---|---|---|---|
Idempotency-Key | header | Yes | Use a stable request identifier. Contact writes replay the saved response for seven days, subject to current resource access. Subscription creation retains its identity for seven days but returns current metadata and signing secret; a disabled/deleted subscription rejects replay. Reusing those keys with a different operation, path or exact JSON body returns 409. Subscription status and delete operations are repeatable state changes. Type: string minLength: 8 maxLength: 128 pattern: "^[!-~]+$" |
Request body (required)
application/json
$ref: '#/components/schemas/ContactCreate'
Models: ContactCreate.
Responses
201 — Contact record.
application/json
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/Contact'
rate_limit:
$ref: '#/components/schemas/RateLimit'
Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.
Error format, status codes, and retry guidance.
Create or update a contact by external identity
POST /v1/contacts/upsert
Requires contacts.read and contacts.write. The exact case-sensitive external_source and external_id pair is saved within this authorized connection. Use a stable source app/account identifier. A new pair creates an ordinary editable contact in the connection scope; a known pair updates that contact using the same field and audience rules as PATCH. Omitted display_name is derived automatically. A new pair requires a nonnull valid E.164 phone. For a known pair, phone may be omitted when the mapped contact already has a valid saved phone. Setting phone to null follows PATCH promotion rules; it never removes the contact's last usable phone. Missing phone on creation returns CONTACT_PHONE_REQUIRED. No phone/email matching or automatic linking to existing contacts occurs. The mapping persists beyond the seven-day request replay cache. Use a different Idempotency-Key for each source change event. A deleted, merged-away, or inaccessible mapped contact returns 404; source-managed contacts reject writes. A new connection has separate mappings. Source fields never grant additional access.
Parameters
| Name | Location | Required | Description and constraints |
|---|---|---|---|
Idempotency-Key | header | Yes | Use a stable request identifier. Contact writes replay the saved response for seven days, subject to current resource access. Subscription creation retains its identity for seven days but returns current metadata and signing secret; a disabled/deleted subscription rejects replay. Reusing those keys with a different operation, path or exact JSON body returns 409. Subscription status and delete operations are repeatable state changes. Type: string minLength: 8 maxLength: 128 pattern: "^[!-~]+$" |
Request body (required)
application/json
$ref: '#/components/schemas/ContactUpsert'
Models: ContactUpsert.
Responses
200 — Contact record.
application/json
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/Contact'
rate_limit:
$ref: '#/components/schemas/RateLimit'
Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.
Error format, status codes, and retry guidance.
Get one accessible contact
GET /v1/contacts/{id}
Requires contacts.read. Missing and inaccessible resources return 404.
Parameters
| Name | Location | Required | Description and constraints |
|---|---|---|---|
id | path | Yes | Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$" |
Responses
200 — Contact record.
application/json
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/Contact'
rate_limit:
$ref: '#/components/schemas/RateLimit'
Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.
Error format, status codes, and retry guidance.
Update selected fields of an accessible contact
PATCH /v1/contacts/{id}
Requires contacts.read and contacts.write. Omitted fields are unchanged. Nullable fields accept null to clear the value, subject to the phone requirement. Phone/email changes retain other saved channels. The resulting contact must have a valid saved phone: omitted phone preserves it; phone=null promotes another saved phone to primary and retains the previous phone as a secondary number. If no other valid phone remains, the update fails with CONTACT_PHONE_REQUIRED without changing the contact. Email=null clears the primary email designation. display_name cannot be cleared explicitly. When omitted, name or primary-channel edits recompute it from the resulting first/last name, company, phone, or email, falling back to Unknown Contact. Unrelated edits preserve it. Source-managed read-only contacts reject changes. No resource_version or updated_at write is accepted.
Parameters
| Name | Location | Required | Description and constraints |
|---|---|---|---|
id | path | Yes | Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$" |
Idempotency-Key | header | Yes | Use a stable request identifier. Contact writes replay the saved response for seven days, subject to current resource access. Subscription creation retains its identity for seven days but returns current metadata and signing secret; a disabled/deleted subscription rejects replay. Reusing those keys with a different operation, path or exact JSON body returns 409. Subscription status and delete operations are repeatable state changes. Type: string minLength: 8 maxLength: 128 pattern: "^[!-~]+$" |
Request body (required)
application/json
$ref: '#/components/schemas/ContactWrite'
Models: ContactWrite.
Responses
200 — Contact record.
application/json
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/Contact'
rate_limit:
$ref: '#/components/schemas/RateLimit'
Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.
Error format, status codes, and retry guidance.
List contact notes
GET /v1/contacts/{id}/notes
Requires contacts.read. Missing and inaccessible parent resources return 404.
Parameters
| Name | Location | Required | Description and constraints |
|---|---|---|---|
id | path | Yes | Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$" |
limit | query | No | Type: integer default: 50 minimum: 1 maximum: 100 |
cursor | query | No | Opaque cursor from next_cursor. Reuse the same credentials, endpoint, filters, ordering, and limit. Results may change between requests; pagination does not freeze the dataset. Type: string |
Responses
200 — Page ordered by ascending ID.
application/json
type: object
required:
- data
- next_cursor
properties:
data:
type: array
items:
$ref: '#/components/schemas/Note'
next_cursor:
type:
- string
- 'null'
Models: Note.
Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.
Error format, status codes, and retry guidance.
Add a contact note
POST /v1/contacts/{id}/notes
Requires contacts.read and contacts.write. Idempotency lasts seven days. Source-managed read-only contacts reject writes.
Parameters
| Name | Location | Required | Description and constraints |
|---|---|---|---|
id | path | Yes | Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$" |
Idempotency-Key | header | Yes | Use a stable request identifier. Contact writes replay the saved response for seven days, subject to current resource access. Subscription creation retains its identity for seven days but returns current metadata and signing secret; a disabled/deleted subscription rejects replay. Reusing those keys with a different operation, path or exact JSON body returns 409. Subscription status and delete operations are repeatable state changes. Type: string minLength: 8 maxLength: 128 pattern: "^[!-~]+$" |
Request body (required)
application/json
type: object
required:
- body
properties:
body:
type: string
minLength: 1
maxLength: 10000
additionalProperties: false
Responses
201 — Authorized result.
application/json
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/Note'
Models: Note.
Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.
Error format, status codes, and retry guidance.
Update a contact note
PATCH /v1/contacts/{id}/notes/{note_id}
Requires contacts.read and contacts.write. Idempotency lasts seven days. Source-managed read-only contacts reject writes.
Parameters
| Name | Location | Required | Description and constraints |
|---|---|---|---|
id | path | Yes | Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$" |
note_id | path | Yes | Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$" |
Idempotency-Key | header | Yes | Use a stable request identifier. Contact writes replay the saved response for seven days, subject to current resource access. Subscription creation retains its identity for seven days but returns current metadata and signing secret; a disabled/deleted subscription rejects replay. Reusing those keys with a different operation, path or exact JSON body returns 409. Subscription status and delete operations are repeatable state changes. Type: string minLength: 8 maxLength: 128 pattern: "^[!-~]+$" |
Request body (required)
application/json
type: object
required:
- body
properties:
body:
type: string
minLength: 1
maxLength: 10000
additionalProperties: false
Responses
200 — Authorized result.
application/json
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/Note'
Models: Note.
Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.
Error format, status codes, and retry guidance.
Soft-delete a contact note
DELETE /v1/contacts/{id}/notes/{note_id}
Requires contacts.read and contacts.write. Idempotency lasts seven days. Source-managed read-only contacts reject writes.
Parameters
| Name | Location | Required | Description and constraints |
|---|---|---|---|
id | path | Yes | Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$" |
note_id | path | Yes | Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$" |
Idempotency-Key | header | Yes | Use a stable request identifier. Contact writes replay the saved response for seven days, subject to current resource access. Subscription creation retains its identity for seven days but returns current metadata and signing secret; a disabled/deleted subscription rejects replay. Reusing those keys with a different operation, path or exact JSON body returns 409. Subscription status and delete operations are repeatable state changes. Type: string minLength: 8 maxLength: 128 pattern: "^[!-~]+$" |
Responses
200 — Authorized result.
application/json
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/Note'
Models: Note.
Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.
Error format, status codes, and retry guidance.
List existing workspace contact property definitions
GET /v1/contact-properties
Requires contacts.read. Missing and inaccessible parent resources return 404.
Parameters
| Name | Location | Required | Description and constraints |
|---|---|---|---|
limit | query | No | Type: integer default: 50 minimum: 1 maximum: 100 |
cursor | query | No | Opaque cursor from next_cursor. Reuse the same credentials, endpoint, filters, ordering, and limit. Results may change between requests; pagination does not freeze the dataset. Type: string |
Responses
200 — Page ordered by ascending ID.
application/json
type: object
required:
- data
- next_cursor
properties:
data:
type: array
items:
$ref: '#/components/schemas/ContactPropertyDefinition'
next_cursor:
type:
- string
- 'null'
Models: ContactPropertyDefinition.
Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.
Error format, status codes, and retry guidance.
List contact property values
GET /v1/contacts/{id}/properties
Requires contacts.read. Missing and inaccessible parent resources return 404.
Parameters
| Name | Location | Required | Description and constraints |
|---|---|---|---|
id | path | Yes | Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$" |
limit | query | No | Type: integer default: 50 minimum: 1 maximum: 100 |
cursor | query | No | Opaque cursor from next_cursor. Reuse the same credentials, endpoint, filters, ordering, and limit. Results may change between requests; pagination does not freeze the dataset. Type: string |
Responses
200 — Page ordered by ascending ID.
application/json
type: object
required:
- data
- next_cursor
properties:
data:
type: array
items:
$ref: '#/components/schemas/ContactPropertyValue'
next_cursor:
type:
- string
- 'null'
Models: ContactPropertyValue.
Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.
Error format, status codes, and retry guidance.
Set a typed value on an existing contact property
PATCH /v1/contacts/{id}/properties/{property_id}
Requires contacts.read and contacts.write. Idempotency lasts seven days. Source-managed read-only contacts reject writes.
Parameters
| Name | Location | Required | Description and constraints |
|---|---|---|---|
id | path | Yes | Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$" |
property_id | path | Yes | Stable existing Waveline record ID. Keep it as a string; it may exceed JavaScript's safe integer range. Type: string maxLength: 19 pattern: "^[1-9][0-9]*$" |
Idempotency-Key | header | Yes | Use a stable request identifier. Contact writes replay the saved response for seven days, subject to current resource access. Subscription creation retains its identity for seven days but returns current metadata and signing secret; a disabled/deleted subscription rejects replay. Reusing those keys with a different operation, path or exact JSON body returns 409. Subscription status and delete operations are repeatable state changes. Type: string minLength: 8 maxLength: 128 pattern: "^[!-~]+$" |
Request body (required)
application/json
type: object
required:
- value
properties:
value:
description: >-
Must match the existing definition type. Null clears. Dates use YYYY-MM-DD, phone
uses E.164, multi-select uses defined choices.
additionalProperties: false
Responses
200 — Authorized result.
application/json
type: object
required:
- data
properties:
data:
$ref: '#/components/schemas/ContactPropertyValue'
Models: ContactPropertyValue.
Errors — 400 invalid input, 401 expired/revoked/missing key, 403 scope or access denied, 404 missing/inaccessible, 409 request/state conflict, 413 oversized request or response, 415 wrong content type, 429 rate limited, or 503 unavailable. 429 includes Retry-After. All responses include X-Request-Id.