X-Sender-Tenant and X-API-Key headers. The base URL is https://api.senderz.app/api/v1.
Identity rules
A profile is identified by email, phone, or contact id. Email and phone
are each unique per workspace among non-deleted contacts.
- A contact needs at least one identifier. Create and upsert reject a body with neither an email nor a phone, with
400and codecontact_identifier_required. - Phone-only profiles are fully supported. Email is only validated when you actually send a non-empty value.
- Emails are lowercased and trimmed on every write and on lookup, so
Dana@Example.comanddana@example.comare the same contact. - Phones are normalized to E.164. A leading
00becomes+, and Israeli local forms (050-123-4567,0501234567,501234567) resolve to+972501234567. Spaces, hyphens, dots and parentheses are stripped. - A phone that cannot be normalized is rejected, not silently dropped:
400with codecontact_phone_invalid.
The profile object
Every endpoint on this page returns this object, wrapped in the standard{ "success": true, "data": ... } envelope.
string
Contact UUID.
string | null
Lowercased email address. Null on a phone-only profile.
string | null
E.164 phone number, for example
+972501234567.string | null
string | null
string | null
IANA timezone name, for example
Asia/Jerusalem.string | null
Language tag, for example
he or en.string
One of
subscribed, unsubscribed, never_subscribed, cleaned. Defaults to never_subscribed.string
Same four values. Defaults to
never_subscribed.string
Same four values. Defaults to
never_subscribed.string
Same four values. Defaults to
never_subscribed.string | null
ISO 8601 timestamp of the last email consent change.
string | null
string | null
string | null
string
One of
valid, invalid, risky, unknown. Defaults to unknown. Read-only: it is set by list validation during import, never by these endpoints.object | null
Free-form key and value store. See Custom properties.
string[] | null
string | null
Read-only. Assigned by RFM analysis, null until the contact has been scored.
string | null
boolean
Read-only. True when the contact’s email address is on the workspace suppression list (hard bounce, spam complaint, blacklist, or an unsubscribe). A suppressed address is skipped at send time regardless of
emailConsent.string | null
Read-only. ISO 8601 timestamp set by the recipient from the preference center when they choose to pause rather than unsubscribe. While it is in the future, marketing sends to this contact are skipped even when
emailConsent is subscribed. There is deliberately no API to lift a pause early: the recipient clears it by opting in again, or it expires on its own.string
ISO 8601 timestamp.
string
ISO 8601 timestamp.
Example profile
Consent fields
Four channels carry independent consent:emailConsent, smsConsent, pushConsent and whatsappConsent. Each accepts the same four states.
Setting a consent value also writes a consent-log entry and stamps the matching
*ConsentTimestamp. On create and upsert you can attribute the change with emailConsentSource, smsConsentSource, pushConsentSource and whatsappConsentSource (each a string of at most 64 characters). When you omit the source it is recorded as manual. Use api for programmatic writes so the consent record reflects where the opt-in came from.
Omitting a consent field leaves it unchanged. On update, a value identical to the stored one is a no-op and does not restamp the timestamp.
Custom properties
customProperties is a JSON object of arbitrary keys.
- On create, the object is stored as given.
- On update, the object is shallow merged into the stored one. Keys you send win, keys you omit are preserved. There is no delete operation for a single key.
- A key that matches a custom field defined in your workspace is coerced to that field’s type (number, date, boolean, URL, list, or a select option). A value that fails coercion returns
400with acodenaming the problem and afieldnaming the key. - Keys that match no definition are stored exactly as sent.
tags, by contrast, replaces the stored array. Send the full set you want.
List profiles
integer
default:"25"
Page size. Minimum 1, maximum 200. A larger value returns
400.integer
default:"0"
Records to skip. Minimum 0. Takes precedence over
page when both are sent.integer
default:"1"
1-based page number, an alternative to
offset. Minimum 1.string
Case-insensitive substring match against email, phone, first name, last name, and the two names joined. At most 255 characters.
string
Restrict to members of a list or segment, by UUID. Works for both static and dynamic segments. An id that does not exist in your workspace returns an empty page rather than a
404.string
One of
subscribed, unsubscribed, never_subscribed, cleaned.string
email or sms. This only chooses which consent column consent is matched against. It defaults to email and does nothing on its own, so send it together with consent.string
default:"-createdAt"
Sort key. Legal columns are
createdAt, updatedAt, email, firstName, lastName. Prefix with - for descending (-updatedAt), or use column:asc and column:desc. A bare timestamp column defaults to descending, a bare text column to ascending. An unknown column falls back to -createdAt rather than erroring.next_cursor is the next offset as a string, so feed it back as offset, not page.
200 OK
Look up a profile
string
Email address. Required unless
contactId is sent. Trimmed and lowercased before matching. At most 320 characters.string
Contact UUID. When present,
email is ignored.No other query parameter is accepted. Sending one,
phone for example,
returns 400. To resolve a contact by phone, use the
upsert endpoint, which matches on phone.404 with message Profile not found when nothing matches.
Get a profile by ID
string
required
Contact UUID. A value that is not a valid UUID returns
400.404 with code contact_not_found.
Create a profile
string
At most 320 characters. Trimmed and lowercased. Required unless
phone is sent.string
At most 32 characters before normalization. Required unless
email is sent.string
At most 128 characters.
string
At most 128 characters.
string
At most 64 characters.
string
At most 10 characters.
string
subscribed, unsubscribed, never_subscribed or cleaned.string
Same four values.
string
Same four values.
string
Same four values.
string
At most 64 characters. Defaults to
manual.string
At most 64 characters. Defaults to
manual.string
At most 64 characters. Defaults to
manual.string
At most 64 characters. Defaults to
manual.string[]
Array of strings.
object
Free-form object.
string | null
At most 4000 characters. Send
null to clear.Address-style fields
These nine convenience fields are accepted on create, upsert and update-by-email. They are not native columns: each is folded intocustomProperties under the same key before the contact is written, so they come back inside customProperties, not at the top level.
string
At most 255 characters.
string
At most 255 characters.
string
At most 255 characters.
string
At most 64 characters.
string
At most 500 characters.
string
At most 500 characters.
string
At most 32 characters.
string
At most 64 characters.
string
At most 128 characters.
201 with the profile object.
201 Created
contact.created outbound webhook and enrolls the contact in any automation triggered by a new contact.
Create errors
Upsert a profile
At least one of
email or phone is required. A body with neither returns
400 with the message email or phone is required.customProperties is merged, tags is replaced, and an omitted field is left alone. When nothing matches, a new contact is created.
200 with the profile object, whether it was created or updated.
Update a profile by email
404 when the email does not resolve.
string
required
The lookup key, not a value to write. At most 320 characters. It is never rewritten unless you also send
newEmail.string
Change the contact’s email address. At most 320 characters.
string
At most 32 characters before normalization.
string
At most 128 characters.
string
At most 128 characters.
string
At most 64 characters.
string
At most 10 characters.
string
subscribed, unsubscribed, never_subscribed or cleaned.string
Same four values.
string
Same four values.
string
Same four values.
string[]
Replaces the stored array.
object
Shallow merged into the stored object.
string | null
At most 4000 characters. Send
null to clear.customProperties.
200 with the profile object.
Update a profile by ID
string
required
Contact UUID. A value that is not a valid UUID returns
400.string
Replaces the contact’s email. At most 320 characters.
string
At most 32 characters before normalization.
string
At most 128 characters.
string
At most 128 characters.
string
At most 64 characters.
string
At most 10 characters.
string
subscribed, unsubscribed, never_subscribed or cleaned.string
Same four values.
string
Same four values.
string
Same four values.
string
At most 64 characters.
string
At most 64 characters.
string
At most 64 characters.
string
At most 64 characters.
string[]
Replaces the stored array.
object
Shallow merged into the stored object.
string | null
At most 4000 characters. Send
null to clear.200 with the profile object.
Update errors
Both update routes share these:
A successful update fires the
contact.updated outbound webhook.
Blacklist
POST opts the contact out on the chosen channel and adds their identifier to the suppression list. DELETE lifts it and restores the consent state captured at the moment they were blacklisted, rather than blindly re-subscribing them.
Both take the same body.
string
Contact UUID. When present,
email is ignored.string
Email address. Required unless
contactId is sent. Trimmed and lowercased.string
default:"email"
email, sms or both.channel: "sms" on a contact with no phone number returns 400 with the
message Contact has no phone for sms channel. channel: "both" on the same
contact applies the email half and skips SMS without erroring.POST returns 201. DELETE returns 200 with the profile object, not 204.
201 Created
contact.unsubscribed webhook when the email channel was affected, and enrolls the contact in any unsubscribe automation. Lifting a blacklist fires contact.updated.
Errors
Every failure returns the standard envelope, with a machine-readablecode where one exists:
Codes used by this resource:
contact_identifier_required, contact_phone_invalid, contact_not_found, contact_email_taken, contact_phone_taken. A custom-property coercion failure returns a code naming the problem (invalid_number, invalid_date, invalid_boolean, invalid_url, invalid_list, invalid_option, too_long, too_many_items) together with a field naming the offending key.
See Errors and rate limits for the shared envelope and limits, and Lists for adding a profile to a list.
