Skip to Content
ConceptsEvents and triggers

Events and triggers

Every run starts with a trigger — something outside the loop telling the platform to call your run(). The platform packages the trigger’s input into an event and passes it as the first argument:

async run(event: HealthEvent, step: HealthStep) { const payload = event.body; // … }

This page covers the shape of event, the trigger paths available today, and how to type the body for autocomplete.

HealthEvent

type HealthEvent<T = unknown> = { body: T; };

Properties

PropertyTypeDescription
bodyTThe JSON payload from the trigger. undefined if the trigger sent no body.

The platform doesn’t enforce a payload schema — whatever JSON the trigger sent lands on body unchanged. Parameterize HealthEvent with your own type and the entire body is typed inside run.

Triggers

Two paths today.

UI

From a loop’s overview page, click Run. A dialog accepts an optional JSON body; submitting kicks off the run and you’ll see it appear under Runs. Whatever you typed in the dialog becomes event.body.

UI-triggered runs are tagged with the user who submitted them.

API

Mint a token under Settings → API tokens, then POST to the loop’s trigger URL:

curl -X POST https://api.ollie.health/loop/$ORG_ID/$LOOP_ID \ -H "Authorization: Bearer $LOOPS_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "patientId": "abc", "phoneNumber": "+15551234567" }'

The JSON body of the POST becomes event.body. The response returns a runId you can use to find the run later. A token only works for loops in the org that minted it; revoking the token cuts off its access without touching the loops themselves.

API-triggered runs are tagged with the token, not a user.

Access the event

Take event as the first argument to run and read off body:

async run(event: HealthEvent, step: HealthStep) { const { patientId } = event.body; // … }

Whatever is on event.body is recorded as the run’s input and shown on the run detail page — you don’t need to log it separately.

Examples

Type the body for autocomplete

Declare an interface at the top of your loop file and parameterize HealthEvent with it:

interface IntakePayload { patientId: string; phoneNumber: string; source: "kiosk" | "web"; } export class HealthLoop extends Loop { async run(event: HealthEvent<IntakePayload>, step: HealthStep) { const { patientId, phoneNumber, source } = event.body; // all three fields are typed } }

Handle a missing body

If the trigger sent no JSON body, event.body is undefined. Guard early:

async run(event: HealthEvent<IntakePayload>, step: HealthStep) { if (!event.body) { return { error: "no payload" }; } const { patientId } = event.body; // … }

Branch on trigger source

If your loop behaves differently for UI runs versus API runs, encode that on the payload itself rather than trying to read trigger metadata inside run:

interface IntakePayload { patientId: string; source: "manual" | "automation"; } async run(event: HealthEvent<IntakePayload>, step: HealthStep) { if (event.body.source === "manual") { // skip downstream notification for hand-triggered runs } }
Last updated on