Create a contact
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.
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.
code is conflict.
application/problem+json code is internal_error. The
request_id field can be quoted to SendOps support to investigate.
application/problem+json 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 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