Contacts
Manage the people you send to, create, update, segment, and bulk-import contacts.
contacts are the people you send to. Store them once and address a
message by contactId instead of a raw email, or
group them with contact groups for segmented
sends.
What you can do
- Manage contacts, create, retrieve, list, update, and remove.
- Addresses and tags, add or remove one identity or a set of tags without touching the rest.
- Bulk operations, import many contacts at once, and bulk-update status or tags.
- Engagement, read a contact's engagement history.
- Export your contacts.
Addressing a contact
Once a contact exists, reference it from the send body instead of an email address:
{
"to": [{ "contactId": "contact_k9mvt4znw8rh7q2x" }],
"channel": "email",
"email": { "subject": "Hello", "html": "<p>Hi!</p>" }
}Reading and updating a contact
A list returns contact summaries: name, status, first email address, the channels the contact can
be reached on, tags, and lists. Get a contact returns the full
record, with each identity's id, the profile, preferences, and metadata.
Find or create with attributes
Choose exactly one lookup. Attributes under create apply only when the contact is new:
const { contact, created } = await samva.contacts.findOrCreate({
lookup: { type: "email", value: "ada@example.com" },
create: {
name: "Ada Lovelace",
externalId: "user_123",
profile: { firstName: "Ada", customFields: { plan: "creator" } },
},
});Define custom fields before setting them; unknown keys or values of the wrong type are rejected.
An existing contact returns unchanged with created: false, even when create names different
attributes. Use update to change it. To look up by your own
user id and create an email address, use lookup: { type: "externalId", value: "user_123" } and
create.identities: [{ channel: "email", identifier: "ada@example.com" }].
Inspect selected custom fields
Keep list summaries lean by default, or select defined custom field keys and filter by one value:
const page = await samva.contacts.list({
fields: "plan,signup_at",
customField: "plan",
customFieldValue: JSON.stringify("creator"),
});
// page.items contain customFields with only the requested keys that are present.customFieldValue is a JSON scalar: a quoted string, number, boolean, or null. Null matches a
stored null value, not an absent key. Unknown keys, malformed values, and incomplete filters fail
with ValidationError. Pagination uses the same filters on every page.
From the CLI, use samva contacts list --fields plan,signup_at --json
or samva contacts get <contact-id> --json.
Update a contact changes only the fields you name. Metadata
merges by key, and a null value deletes that key:
{ "metadata": { "plan": "pro", "trialEndsAt": null } }Identities and tags are never replaced by an update. Add an address with
POST /v1/contacts/{id}/identities and remove one by
its id with DELETE /v1/contacts/{id}/identities/{identityId}.
Tags change through POST /v1/contacts/{id}/tags and
DELETE /v1/contacts/{id}/tags.
Shared conventions (auth, status codes, errors, pagination) are in the REST API reference. Per-operation request and response detail is in the sidebar.