Contact Properties
Define custom fields with specific types to store structured data on your subscribers. Properties let you go beyond free-form metadata with validated, typed fields like company name, plan tier, signup date, and more.
Overview
Contact properties are organization-level field definitions that can hold typed values per subscriber. Unlike the free-form metadata JSON field, properties have explicit types and validation.
| Type | Description | Example |
|---|---|---|
| text | Free-form text (max 10,000 chars) | "Acme Inc" |
| number | Numeric value (integer or decimal) | 42, 3.14 |
| date | ISO 8601 date/datetime | "2025-06-15T00:00:00Z" |
| boolean | True or false | true, false |
| enum | One of a predefined set of options | "starter", "pro", "enterprise" |
Limits: Maximum 50 properties per organization. Property keys must start with a letter or underscore and contain only alphanumeric characters and underscores. Keys are unique per organization and cannot be changed after creation.
Create a Property
POST /v1/propertiesRequest Body
| Parameter | Type | Description |
|---|---|---|
| key | string | Unique key (required, max 64 chars, alphanumeric + underscore) |
| name | string | Display name (required, max 128 chars) |
| type | string | text, number, date, boolean, or enum (default: text) |
| description | string | Description (max 256 chars) |
| required | boolean | Whether the property is required (default: false) |
| enumOptions | string[] | Options for enum type (required for enum, max 50 options) |
| sortOrder | number | Display order (default: 0) |
Example
// Text property
const company = await client.properties.create({
key: 'company',
name: 'Company',
type: 'text',
description: 'The subscriber\'s company name',
});
// Enum property with predefined options
const plan = await client.properties.create({
key: 'plan_tier',
name: 'Plan Tier',
type: 'enum',
enumOptions: ['starter', 'pro', 'enterprise'],
required: true,
});
// Boolean property
await client.properties.create({
key: 'marketing_consent',
name: 'Marketing Consent',
type: 'boolean',
});
// Date property
await client.properties.create({
key: 'trial_ends_at',
name: 'Trial End Date',
type: 'date',
});List Properties
GET /v1/propertiesQuery Parameters
| Parameter | Type | Description |
|---|---|---|
| active | boolean | Filter by active status |
const { data: properties } = await client.properties.list();
// [{ id, key, name, type, description, required, enumOptions, active, ... }]
// List only active properties
const { data: active } = await client.properties.list({ active: true });Update & Delete Properties
PATCH /v1/properties/:idDELETE /v1/properties/:idYou can update the display name, description, required flag, and enum options. The key and type cannot be changed after creation. Deleting performs a soft delete (sets active: false), preserving existing data.
// Update a property
await client.properties.update('prop_xxx', {
name: 'Organization',
description: 'Updated description',
});
// Add enum options
await client.properties.update('prop_xxx', {
enumOptions: ['starter', 'pro', 'enterprise', 'custom'],
});
// Deactivate a property (soft delete)
await client.properties.delete('prop_xxx');
// Reactivate a property
await client.properties.update('prop_xxx', { active: true });Subscriber Property Values
Set and retrieve property values for individual subscribers.
GET /v1/audiences/:audienceId/subscribers/:subscriberId/propertiesPUT /v1/audiences/:audienceId/subscribers/:subscriberId/propertiesGet Values
const { data: values } = await client.properties.getValues(
'aud_xxx',
'sub_xxx'
);
// {
// company: { value: 'Acme Inc', type: 'text', name: 'Company' },
// plan_tier: { value: 'enterprise', type: 'enum', name: 'Plan Tier' },
// }Set Values
Send a JSON object of property key-value pairs. Values are validated against the property type. Set a value to null to delete it.
// Set property values (merges with existing)
await client.properties.setValues('aud_xxx', 'sub_xxx', {
company: 'Acme Inc',
plan_tier: 'enterprise',
marketing_consent: true,
trial_ends_at: '2025-07-01T00:00:00Z',
});
// Delete a property value
await client.properties.setValues('aud_xxx', 'sub_xxx', {
trial_ends_at: null,
});Python SDK
import veilmail
client = veilmail.VeilMail("veil_live_xxxxx")
# Create a property
client.properties.create(
key="company",
name="Company",
type="text",
)
# List properties
result = client.properties.list()
# Set subscriber values
client.properties.set_values(
"aud_xxx",
"sub_xxx",
{"company": "Acme Inc", "plan_tier": "enterprise"},
)
# Get subscriber values
values = client.properties.get_values("aud_xxx", "sub_xxx")Using Properties in Templates
Property values can be used as template variables in your emails. Reference them using the property key in double curly braces.
await client.emails.send({
from: 'hello@yourdomain.com',
to: 'user@example.com',
subject: 'Welcome, {{company}}!',
html: `
<h1>Welcome to the {{plan_tier}} plan!</h1>
<p>Your trial ends on {{trial_ends_at}}.</p>
`,
templateData: {
company: 'Acme Inc',
plan_tier: 'Enterprise',
trial_ends_at: 'July 1, 2025',
},
});Required Scopes
| Endpoint | Scope |
|---|---|
| List / Get properties | property:read |
| Create / Update / Delete properties | property:write |
| Get subscriber property values | audience:read |
| Set subscriber property values | audience:write |