Create a contact

Search Documentation

Search across all developer documentation

contacts

Create a contact

POST /v1/contacts
Auth required api.contacts.manage

Creates a new contact in the org's SES contact list.

Returns 409 conflict when an active contact with that email already exists. An archived contact (previously deleted via this API) is reactivated instead and the call returns 201.

Attribute values are validated against the org's registry — an unknown attribute key or a type mismatch returns 422 attribute_validation_failed with a per-attribute errors[] array.

Supports the Idempotency-Key header: a duplicate key within 24 h replays the original response.

Request body

Content type: application/json

email string<email> required
attributes object optional

Custom attribute map. Values are native JSON, validated and coerced against the org's attribute registry. An unknown key or a type mismatch returns 422 attribute_validation_failed.

unsubscribe_all boolean optional

Master opt-out. Defaults to false.

topic_preferences array<object> optional

Each entry in topic_preferences:

topic_name string required

The topic's name (see /v1/topics).

subscription_status string enum required

One of: OPT_IN, OPT_OUT

lists object optional

Inline static-List membership directive carried on POST and PATCH contact writes. add/remove apply a delta; set, when present, replaces membership declaratively (an empty set clears all memberships). List ids must name static Lists in the org.

Fields of lists:

add array<string<uuid>> optional

List ids to add the contact to.

remove array<string<uuid>> optional

List ids to remove the contact from.

set array<string<uuid>> optional

When present, replaces the contact's static-List membership exactly. An empty array clears all memberships. Mutually exclusive with the add/remove delta.

Responses

Errors follow the RFC 7807 problem format — see the error reference.

201 Contact created (or archived contact reactivated) application/json
email string<email> required
unsubscribe_all boolean required

Master opt-out — when true the contact receives no topic email.

attributes object required

Custom attribute map as stored in SES. Always an object, never null.

topic_preferences array<object> required

Each entry in topic_preferences:

topic_name string required

The topic's name (see /v1/topics).

subscription_status string enum required

One of: OPT_IN, OPT_OUT

lists array<string<uuid>> required

Ids of the static Lists the contact currently belongs to. Always an array.

created_at string<date-time> required
updated_at string<date-time> required
ses_sync object optional

Present ONLY while the contact has not reached the customer's SES account yet; absent means synced. A 2xx on a contact write means SendOps has accepted and durably stored the change, not that SES has it — writes are mirrored afterwards, paced against the AWS account-wide budget of one contact request per second. Everything user-visible (segments, consent filtering, exports, sends) reads SendOps' own copy, so a pending mirror holds nothing back.

401 Missing, malformed, or unknown API key application/problem+json
403 Key lacks the required scope or plan limit violated application/problem+json
404 Resource not found application/problem+json
409 The mutation is rejected by a state rule rather than a bad request — e.g. editing a git-backed (read-only) definition. The code is conflict. application/problem+json
422 Query parameter or path value failed validation application/problem+json
429 Per-org rate limit exceeded application/problem+json
500 Unexpected server-side failure. The code is internal_error. The request_id field can be quoted to SendOps support to investigate. application/problem+json
503 An upstream dependency — the AWS SES account connected to this org — refused, throttled, or did not answer in time. The code is upstream_unavailable; the status is 503 when the call failed and 504 when it exceeded our deadline. Retryable. Always accompanied by Retry-After. On a 504 the outcome is genuinely unknown (the write may have reached SES), so retries of a non-idempotent call should carry an Idempotency-Key. The detail string is deliberately generic: the underlying AWS error describes the customer's own account and is recorded in SendOps logs against the request_id, not returned here. application/problem+json
504 An upstream dependency — the AWS SES account connected to this org — refused, throttled, or did not answer in time. The code is upstream_unavailable; the status is 503 when the call failed and 504 when it exceeded our deadline. Retryable. Always accompanied by Retry-After. On a 504 the outcome is genuinely unknown (the write may have reached SES), so retries of a non-idempotent call should carry an Idempotency-Key. The detail string is deliberately generic: the underlying AWS error describes the customer's own account and is recorded in SendOps logs against the request_id, not returned here. application/problem+json