journey.step.completed.v1
Fired when a person completes, skips, or fails a step in a journey sequence.
Event Name
journey.step.completed.v1Schema
| Field | Type | Required | Description |
|---|---|---|---|
eventId | uuid | Yes | Unique event identifier |
occurredAt | datetime | Yes | ISO 8601 timestamp |
brandId | uuid | Yes | Brand that owns the journey |
personId | uuid | Yes | Person who completed the step |
journeyId | uuid | Yes | Journey identifier |
journeyRunId | uuid | Yes | Specific journey run instance |
stepId | string | Yes | Step identifier within the journey |
outcome | enum | Yes | Step outcome: completed, skipped, failed |
nextStepId | string | null | No | Next step ID (null if this was the final step) |
Example Payload
{
"eventId": "770a0600-a4bd-63f6-c938-668877662222",
"occurredAt": "2026-05-19T16:30:00Z",
"brandId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"personId": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
"journeyId": "c3d4e5f6-a7b8-9012-cdef-123456789012",
"journeyRunId": "d4e5f6a7-b8c9-0123-defa-234567890123",
"stepId": "step_welcome_email",
"outcome": "completed",
"nextStepId": "step_day3_followup"
}Usage
import { validateEvent } from '@loop/contracts';
const payload = {
eventId: crypto.randomUUID(),
occurredAt: new Date().toISOString(),
brandId: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
personId: 'b2c3d4e5-f6a7-8901-bcde-f12345678901',
journeyId: 'c3d4e5f6-a7b8-9012-cdef-123456789012',
journeyRunId: 'd4e5f6a7-b8c9-0123-defa-234567890123',
stepId: 'step_welcome_email',
outcome: 'completed',
nextStepId: 'step_day3_followup',
};
const result = validateEvent('journey.step.completed.v1', payload);
if (result.success) {
await fetch('https://comms.loop.health/api/events', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`,
},
body: JSON.stringify({
event: 'journey.step.completed.v1',
payload: result.data,
}),
});
} else {
console.error('Validation failed:', result.error.issues);
}