Contact properties
Declare the custom fields your contacts carry, give them a type and a fallback value, and manage them from the API or the dashboard.
Contact properties are the custom fields your contacts carry — plan, company,
renews_on — and the {{variables}} they render as in a broadcast. This page is about
the definitions; setting a value on a contact is covered in
Contacts.
Declaring a property is optional. A property you send on a contact that has never been
declared registers itself as a string, so an existing integration needs no changes. Declaring
one up front is what gives it a type and a fallback value.
Definitions belong to the organization, not to one audience: signup_plan means the same
thing wherever the contact sits, so a contact moved between lists does not change shape.
Properties are also listed in the dashboard under Audiences → Properties, where the same add, edit and delete operations are available.
Fallback values
A declared property can carry a fallback, substituted into {{key}} for contacts that
have no value of their own:
curl -X POST https://api.eusend.dev/contact-properties \
-H "Authorization: Bearer eu_live_xxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-d '{ "key": "plan", "type": "string", "fallback_value": "free" }'This is the durable version of the inline {{plan|free}} syntax: set it once and every
broadcast picks it up, instead of each body having to remember the default. A property
with no fallback renders empty, which is what an unset {{key}} has always done.
Types
string or number. Values are carried and rendered as strings either way — number is
a write-time check, not a formatting instruction. A merge tag has nowhere to say which
thousands separator or which locale it wanted, so {{order_total}} substitutes exactly
the characters you sent. What the type buys is catching a mistyped value where it is cheap
to fix, rather than in an email somebody already received.
/contact-properties| Parameter | Type | Description |
|---|---|---|
keyrequired | string | Lowercase letters, digits and underscores, starting with a letter, at most 40 characters. "email", "name", "first_name", "last_name" and "full_name" are built in and are rejected. Cannot be changed later. |
type | string | "string" (default) or "number". Cannot be changed later. |
fallback_value | string | null | Rendered for contacts carrying no value of their own. Must match the type. At most 500 characters. |
Returns 409 CONFLICT if a property with that name already exists. An organization can
define up to 100 properties.
/contact-propertiesEvery property the organization has defined, ordered by name. Unpaginated — the registry is capped at 100 entries, and a variable picker wants all of them.
{
"data": [
{
"id": "b6d24b8e-af0b-4c3c-be0c-359bbd97381e",
"key": "plan",
"type": "string",
"fallbackValue": "free",
"createdAt": "2026-09-21T10:14:52.110Z",
"updatedAt": "2026-09-21T10:14:52.110Z"
}
]
}/contact-properties/:idOne property definition by id.
/contact-properties/:id| Parameter | Type | Description |
|---|---|---|
fallback_valuerequired | string | null | The new fallback. Send null to clear it. |
The name and type cannot be changed. Renaming would have to rewrite every contact and every broadcast body that spells the old merge tag — and we will not edit your drafts. Changing either is a delete and a create, done deliberately.
/contact-properties/:idDeletes the definition and strips the key from every contact in the organization. The response reports how many contacts were rewritten:
{ "deleted": true, "contactsUpdated": 1240 }This cannot be undone. Leaving the values behind was the alternative and a worse one: the property would keep rendering in broadcasts, and the next contact write carrying that key would register it again — so a delete you watched succeed would silently undo itself. Any broadcast using that merge tag renders it empty afterwards.
In the dashboard
Audiences → Properties lists every declared property with the number of contacts using it, and is where you add, edit and delete them. The count is what makes a delete legible: removing a field nothing uses is a different decision from removing one on 1,240 people.
SDKs
await eusend.contactProperties.create({ key: 'plan', type: 'string', fallbackValue: 'free' })
const { data } = await eusend.contactProperties.list()eusend.ContactProperties.create({"key": "plan", "type": "string", "fallback_value": "free"})
properties = eusend.ContactProperties.list()fallback := "free"
client.ContactProperties.Create(&eusend.CreateContactPropertyRequest{
Key: "plan",
Type: "string",
FallbackValue: &fallback,
})