Upsert a contact
addContact creates the contact, or — if it already exists — overwrites just the fields you provide:
typescript
const { contact, created, suppressed } = await client.addContact({
email: 'user@example.com',
firstName: 'Ada',
tags: ['customer', 'beta'], // auto-created on first use
});
// Partial update: lastName and tags stay untouched
await client.updateContact({ email: 'user@example.com', firstName: 'Grace' });
// Tag management
await client.addTag({ email: 'user@example.com', tag: 'vip' });
await client.removeTag({ email: 'user@example.com', tag: 'beta' });
// Lookup and deletion
const { contact: found } = await client.getContact({ email: 'user@example.com' });
await client.deleteContact({ email: 'user@example.com' });Method reference
| Method | Behavior | 404 if missing? |
|---|---|---|
| addContact | Upsert by email; merges provided fields, adds tags | no — creates |
| updateContact | Same merge semantics, but the contact must exist | yes |
| getContact | Fetch a contact with its tags | yes |
| deleteContact | Permanent delete; idempotent | no — reports deleted: false |
| addTag | Attach one tag (auto-created); idempotent | yes |
| removeTag | Detach one tag; idempotent | yes |
Semantics to know
- Merge, never erase.
addContact/updateContactonly overwrite fields you provide, and tags merge additively. The only way to remove a tag isremoveTag; the only way to remove a contact isdeleteContact. - Tags auto-create with a deterministic dashboard color. Names are case-sensitive —
VIPandvipare different tags. Max 50 tags per call, 100 chars each. - Emails are normalized server-side (trimmed, lowercased) before matching.
Suppression blocks sends, not data.
suppressed: true in responses means the address is on your workspace suppression list (unsubscribed, bounced, or complained). You can still manage the contact's data, but send() calls to that address are rejected with 422.
Prefer raw HTTP? The same operations are exposed as REST endpoints — see the contacts API reference.