# Custom Events

Source: https://wraps.dev/docs/guides/custom-events

Emit events from your application to trigger workflows, track contact activity, and resume waiting automation steps.

5 min read

## Overview

Custom events let you send signals from your application to the Wraps platform. When an event is received, three things happen:

Stored

The event is recorded on the contact's timeline with a 2-year TTL.

Workflows triggered

Any enabled workflow with an `event.received` trigger matching this event name starts a new execution.

Waiting steps resumed

Any workflow execution waiting for this event on this contact resumes down the “yes” branch.

## Sending an Event

Use `client.track()` from the `@wraps.dev/client` SDK to send events with full type safety.

send-event.ts

```
import { createPlatformClient } from '@wraps.dev/client';const client = createPlatformClient({  apiKey: process.env.WRAPS_API_KEY,});const result = await client.track('purchase.completed', {  contactEmail: 'alice@example.com',  properties: {    orderId: 'ord_12345',    amount: 99.00,    plan: 'pro',  },});// { success: true, contactCreated: false, workflowsTriggered: 1, executionsResumed: 0 }
```

## Track Options

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| name | string | Yes | First argument to `track()`. Event name, e.g. `purchase.completed`. Max 255 chars. |
| contactId | string | One of `contactId` or `contactEmail` | Contact ID to associate the event with. |
| contactEmail | string | Alternative to `contactId` | Look up the contact by email address. |
| contactName | string | No | Sets `firstName` when `createIfMissing` creates a new contact. |
| createIfMissing | boolean | No | If `true` and the contact doesn't exist, create one from `contactEmail`. Defaults to `false`. |
| properties | object | No | Arbitrary key-value data attached to the event. Passed to workflows as event data. |

## Auto-Creating Contacts

Set `createIfMissing: true` to create a new contact when one doesn't exist. Requires `contactEmail`. The response includes `contactCreated: true` when a contact was created.

create-if-missing.ts

```
await client.track('signup.completed', {  contactEmail: 'new-user@example.com',  contactName: 'Alice',  createIfMissing: true,  properties: {    source: 'website',    referrer: 'producthunt',  },});
```

**Tip:** This is useful for signup flows where the contact may not exist in Wraps yet. The event fires, the contact is created, and any matching workflows start immediately.

## Batch Events

Send multiple events in a single request with `client.trackBatch()`. Contact resolution, workflow matching, and execution resumption are all batched for efficiency.

batch-events.ts

```
const result = await client.trackBatch([  {    name: 'page.viewed',    contactEmail: 'alice@example.com',    properties: { page: '/pricing' },  },  {    name: 'page.viewed',    contactEmail: 'bob@example.com',    properties: { page: '/docs' },  },  {    name: 'feature.used',    contactId: 'con_abc123',    properties: { feature: 'api-keys' },  },]);// { success: true, processed: 3, workflowsTriggered: 0, executionsResumed: 0, errors: [] }
```

**Note:** Batch does not support `createIfMissing`. Contacts must already exist when using the batch endpoint.

## How Events Connect to Workflows

Events integrate with workflows in two ways:

1\. Trigger new workflows

Create a workflow with the `event.received` trigger type and set the event name to match. When the event fires for a contact, a new workflow execution begins.

The event's `properties` are passed as event data and available in workflow step conditions.

2\. Resume waiting steps

Workflows can include a “Wait for Event” step that pauses execution until a specific event is received for that contact. When the matching event arrives, execution resumes down the “yes” branch. If the timeout expires first, it takes the “no” branch.

## Event Limits

Custom events are unmetered on every plan that has the feature — there is no monthly quota, grace period, or hard cap. Free doesn't include event tracking; Pro and Business do.

## Examples

### Next.js checkout handler

Emit an event after processing a payment to kick off a post-purchase workflow.

app/api/checkout/route.ts

```
// app/api/checkout/route.tsimport { createPlatformClient } from '@wraps.dev/client';import { NextResponse } from 'next/server';const client = createPlatformClient({  apiKey: process.env.WRAPS_API_KEY,});export async function POST(request: Request) {  const { email, plan, orderId } = await request.json();  // ... process payment ...  // Emit event to trigger post-purchase workflow  await client.track('purchase.completed', {    contactEmail: email,    createIfMissing: true,    properties: { plan, orderId },  });  return NextResponse.json({ success: true });}
```

### Stripe webhook handler

Forward Stripe subscription events to Wraps for lifecycle automation.

app/api/stripe-webhook/route.ts

```
// app/api/stripe-webhook/route.tsimport { createPlatformClient } from '@wraps.dev/client';import { NextResponse } from 'next/server';const client = createPlatformClient({  apiKey: process.env.WRAPS_API_KEY,});export async function POST(request: Request) {  const event = await request.json();  switch (event.type) {    case 'customer.subscription.created':      await client.track('subscription.created', {        contactEmail: event.data.object.customer_email,        createIfMissing: true,        properties: {          plan: event.data.object.items.data[0].price.id,          stripeCustomerId: event.data.object.customer,        },      });      break;    case 'customer.subscription.deleted':      await client.track('subscription.cancelled', {        contactEmail: event.data.object.customer_email,        properties: {          cancelReason: event.data.object.cancellation_details?.reason,        },      });      break;  }  return NextResponse.json({ received: true });}
```

## Event Naming Conventions

Use dot-separated `resource.action` names. Keep them lowercase and consistent across your application.

| Event | Use case |
| --- | --- |
| signup.completed | User finished onboarding |
| purchase.completed | Order placed successfully |
| subscription.cancelled | User cancelled their plan |
| feature.used | Specific feature was activated |
| trial.expiring | Trial ends within 3 days |
| invoice.paid | Payment processed successfully |

## Next Steps

Building Workflows

Create automation workflows that trigger on your custom events.

[Workflow guide](https://wraps.dev/docs/guides/workflows)

Webhooks

Receive real-time delivery events at your HTTPS endpoint.

[Webhooks guide](https://wraps.dev/docs/guides/webhooks)
