Skip to Content
ObjectsContact

Contact

What the /contacts endpoints return and what contact.created carries.

Fields on a contact
FieldWhat it is
id stringThe contact's id, a UUID.
object "contact"Always the literal string.
name stringTheir name. Falls back to the email, then the phone, then Contact — never empty.
email string | nullLowercased. Null on a contact known only by phone.
phone string | nullNormalised before storage. Null on a contact known only by email.
tags string[]How the seller segments their list, and what a broadcast is targeted by. Normalised the same way stored tags are.
source string | nullHow they arrived — order, api, signup, and so on. Set once, at creation, and not changed by later activity.
marketingConsentAt string | nullISO 8601 when this person opted in to marketing email, or null if they never did. This is the field that decides whether you may email them. Null is not 'unknown' — it is a customer who bought something and never agreed to be mailed. A timestamp rather than a boolean because 'when' is what a regulator asks.
address objectTheir address, where an order has given us one. Every field is null otherwise.
createdAt string | nullISO 8601.
updatedAt string | nullISO 8601.

marketingConsentAt

This is the field that decides whether you may email this person.

A timestamp means they went through a double opt-in and clicked the link. null means they did not — usually because they are a customer who bought something and was never asked. null is not “unknown”; it is “no”.

A timestamp rather than a boolean because when is what a regulator asks, and because a boolean cannot carry it. An integration pushing contacts into Kit, Mailchimp, GoHighLevel or anything else must filter on this — or use ?consented=true, which does it server-side.

Nothing an API key can send will set it. A contact created through the API always has null here, whatever the request body says; the only way it becomes a timestamp is a person clicking a link in an email. See consent on the contacts endpoint.

Tags

How a seller segments their list, and what a broadcast is targeted by. Changing them changes who a future email reaches, which is why the tag endpoint adds and removes rather than replacing: a tag the seller applied by hand is not something an automation should delete by omitting it.

Tags are normalised — trimmed and lowercased — on write and on filter, so "VIP" and "vip" are one tag.

name is never empty

It falls back to the email, then the phone, then the literal Contact. There is always something to render, so a consumer needs no fallback of its own.

One of email or phone is always present; both may be, and either may be null individually.

source

How this person arrived — order, api, signup, and so on. Set once at creation and not changed by later activity, so a customer who first appeared through an order stays order even after they later opt in through a form.

Treat it as an open enumeration; members are added.

What is deliberately absent

notes. The seller’s private scratchpad about a customer. It is not on this object at all, in either transport — it is the last thing that should sync into a third-party CRM through an integration nobody re-read.

Address

Present on every contact, filled from an order where one has given us an address, every field null otherwise.

Fields on an address object
FieldWhat it is
line1 string | nullStreet address.
line2 string | nullApartment, suite, or whatever the buyer added.
city string | nullCity or town.
region string | nullState, province or county, where the country has one.
postalCode string | nullPostal or ZIP code.
country string | nullISO 3166-1 alpha-2, uppercase.

Example

{ "id": "c1d2e3f4-5a6b-7c8d-9e0f-1a2b3c4d5e6f", "object": "contact", "name": "Ada Lovelace", "email": "ada@example.com", "phone": null, "tags": ["webinar", "vip"], "source": "api", "marketingConsentAt": "2026-08-12T10:03:55.700Z", "address": { "line1": null, "line2": null, "city": null, "region": null, "postalCode": null, "country": null }, "createdAt": "2026-08-12T09:41:07.221Z", "updatedAt": "2026-08-12T10:03:55.700Z" }

POST /contacts returns one extra field on top of this — optInSent, a boolean saying whether the confirmation email actually went out. It is on the write response only, never on a read or a webhook.

Last updated on