Docs/SDK · @ewyn/client/Managing contacts

Managing contacts

Keep your Ewyn audience in sync with your product: upsert contacts as users sign up, tag them as they change plans, and record unsubscribes from your own settings page.

v1 · beta

Upsert a contact

addContact creates the contact, or — if it already exists — overwrites just the fields you provide:

typescript
const { contact, created, suppressed, optOuts } = 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' });

Unsubscribe and resubscribe

When a user changes their email preferences in your product, record it in Ewyn so marketing sends stop. topic is a topic slug from Audience → Topics; omit it for all marketing email.

typescript
// Stop one topic
await client.unsubscribeContact({ email: 'user@example.com', topic: 'newsletter' });

// Stop all marketing email
await client.unsubscribeContact({ email: 'user@example.com' });

// Resume — only when the person asked you to
const { removed, optOuts } = await client.resubscribeContact({
  email: 'user@example.com',
  topic: 'newsletter',
});

// Every contact response lists the marketing opt-outs
const { optOuts: current } = await client.getContact({ email: 'user@example.com' });
// [{ topic: 'newsletter', createdAt: '...' }, { topic: null, createdAt: '...' }]
//   topic: null = all marketing

Both are idempotent: created: false and removed: false report that nothing changed. An unknown or archived topic throws a 404 EwynApiError with code TOPIC_NOT_FOUND. Opt-outs never affect transactional sends — see Subscriptions and unsubscribe.

Method reference

MethodBehavior404 if missing?
addContactUpsert by email; merges provided fields, adds tagsno — creates
updateContactSame merge semantics, but the contact must existyes
getContactFetch a contact with its tagsyes
deleteContactPermanent delete; idempotentno — reports deleted: false
addTagAttach one tag (auto-created); idempotentyes
removeTagDetach one tag; idempotentyes
unsubscribeContactOpt the address out of one topic, or of all marketing; idempotentno — opt-outs are per address (404 for an unknown topic)
resubscribeContactRemove that opt-out; idempotentno — reports removed: false (404 for an unknown topic)

Semantics to know

  • Merge, never erase. addContact / updateContact only overwrite fields you provide, and tags merge additively. The only way to remove a tag is removeTag; the only way to remove a contact is deleteContact.
  • Tags auto-create with a deterministic dashboard color. Names are case-sensitive — VIP and vip are different tags. Max 50 tags per call, 100 chars each.
  • Emails are normalized server-side (trimmed, lowercased) before matching.
Opt-outs stop marketing; suppressions stop delivery.

optOuts lists marketing opt-outs: a send() with type: 'marketing' under one of those topics (or any marketing send after an all-marketing opt-out) is rejected with 422, while transactional sends go through. suppressed: true means a delivery block — hard bounce, spam complaint, or manual suppression. Bounces and manual suppressions block every send, complaints block marketing only, and none of them can be removed through the API. You can still manage the contact's data either way.

Prefer raw HTTP? The same operations are exposed as REST endpoints — see the contacts API reference.

Was this page helpful?