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.

Related documentation

On this page