Contact events (Preview)
Preview · Available to selected organizations. Record custom contact events, retry them safely, and read each contact's event timeline through the SDK, REST API, CLI, or MCP.
Send a custom event when a contact takes an action in your application. Identify the contact with
exactly one of id, externalId, or email. An unmatched email or external ID creates a contact;
an unknown contact ID returns 404.
Record an event
import { createClient } from "samva";
const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
const event = await samva.events.create({
contact: { email: "ada@example.com" },
name: "trial.started",
properties: { plan: "starter" },
idempotencyKey: "trial-started-customer-42",
});
console.log(event.id, event.contactId);curl https://api.samva.dev/v1/events \
-H "x-api-key: $SAMVA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"contact":{"email":"ada@example.com"},"name":"trial.started","properties":{"plan":"starter"},"idempotencyKey":"trial-started-customer-42"}'Reuse the same idempotencyKey when retrying. The key belongs to your organization;
a replay returns the original event without updating it or creating another contact. Use a new key
to record a different event.
occurredAt is an optional ISO 8601 instant for when the action happened. The response also includes
recordedAt, when Samva recorded it, and source: api for your custom events or samva for
contact lifecycle events. Event names beginning with contact. or samva. are reserved.
Record a batch
import { createClient } from "samva";
const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
const result = await samva.events.batch({
events: [
{
contact: { externalId: "customer-42" },
name: "trial.started",
idempotencyKey: "trial-started-customer-42",
},
],
});
for (const item of result.results) {
if (item.status === "error") console.error(item.index, item.error);
}Samva validates the whole batch request before processing it. A request that does not match the
event schema is rejected without recording any events. For a valid request, each event reports
its result independently in input order. Inspect results even when the HTTP request succeeds:
one rejected event does not undo accepted events.
Read a contact's timeline
import { createClient } from "samva";
const samva = createClient({ apiKey: process.env.SAMVA_API_KEY! });
const page = await samva.contacts.listEvents({
id: "contact_7q2xk9mvt4znw8rh",
limit: "25",
});
console.log(page.items);
// Supply page.nextCursor as cursor to read the next page, when it is non-null.The timeline includes custom events and contact lifecycle events. Events remain in the timeline for the contact-event retention window. The API reference documents validation and payload limits.
CLI and MCP
samva events send --data @event.json --dry-run --json
samva events send --data @event.json --json
cat event.json | samva events send --data - --json
samva contacts events contact_7q2xk9mvt4znw8rh --limit 25 --json
samva contacts events contact_7q2xk9mvt4znw8rh --all --jsonl--dry-run validates the request shape offline without recording an event or creating a contact.
The API validates property limits when you submit the event. The timeline command reads one bounded
page by default; --all --jsonl streams every page.
Hosted MCP provides send_contact_event and list_contact_events. The list tool returns one
bounded page of event summaries without properties and a cursor. Both tools use your credential's
organization and API permissions.
Related documentation
Email messaging model
Understand how immediate, scheduled, and campaign email share messages, contacts, conversations, templates, and events.
Journeys (Preview)
Preview · Available to selected organizations. Build automated email sequences that react to contact events, wait, branch, and exit, then publish, pause, and observe them through the SDK, REST API, CLI, or MCP.