Concepts

Contacts & events

The people you send to and the things they do.

Contacts

A contact is a person in a project, identified by email (and optionally your own external_id). Contacts carry custom attributes — arbitrary key/values you set — which you can reference in templates and filter on in automations.

Upserts shallow-merge attributes: sending { "plan": "Pro" } updates plan and leaves other attributes untouched.

curl https://sendandretain.com/api/v1/contacts \
  -H "Authorization: Bearer $AEM_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "[email protected]", "attributes": { "plan": "Pro" } }'
await fetch("https://sendandretain.com/api/v1/contacts", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AEM_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ email: "[email protected]", attributes: { plan: "Pro" } }),
});
import os, requests

requests.post(
    "https://sendandretain.com/api/v1/contacts",
    headers={"Authorization": f"Bearer {os.environ['AEM_KEY']}"},
    json={"email": "[email protected]", "attributes": {"plan": "Pro"}},
)

A timezone attribute (IANA, e.g. America/New_York) controls automation send windows for that contact.

Events

An event is something a contact did — order.completed, trial.started. You emit events through the API; active automations whose trigger and filter match enroll the contact synchronously.

curl https://sendandretain.com/api/v1/events \
  -H "Authorization: Bearer $AEM_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "email": "[email protected]", "name": "order.completed", "properties": { "total": 42.5 } }'
await fetch("https://sendandretain.com/api/v1/events", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.AEM_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    email: "[email protected]",
    name: "order.completed",
    properties: { total: 42.5 },
  }),
});
import os, requests

requests.post(
    "https://sendandretain.com/api/v1/events",
    headers={"Authorization": f"Bearer {os.environ['AEM_KEY']}"},
    json={"email": "[email protected]", "name": "order.completed", "properties": {"total": 42.5}},
)

Emitting an event with an unknown email creates the contact. You can also pass contact_attributes to upsert attributes in the same call, and a dedupe_key to make the event idempotent.

Event properties and contact attributes are both available to automation filters — that's how you target "trial started on the Pro plan".

On this page