Skip to Content
WorkflowsQuick start

Quick start

A scan workflow, from an empty editor to one triggered run. A POST starts it with a patient id and phone number. The run saves a scan link, pauses until the patient finishes or 30 minutes pass, then sends a notification.

1. Create the loop

From your loops list, create a loop and pick New Workflow Loop. Name it health-scan.

You’ll land in the editor with a starter file. Replace it with the workflow below.

2. Write the workflow

import { workflow } from "@loops/sdk"; type ScanRequest = { patientId: string; phoneNumber: string }; export const healthScanWorkflow = workflow<ScanRequest>({ id: "health-scan", execute: async ({ step, event }) => { const { patientId, phoneNumber } = event.body; await step.do("Generate a health scan", async ({ ctx }) => { await ctx.FACIAL_SCAN.create({ metadata: { patientId, phoneNumber }, }); }); const scan = await step.waitForScan({ timeout: "30 minutes" }); await step.do("Send a notification", async () => { await fetch("https://api.example.com/send-notification", { method: "POST", body: JSON.stringify({ patientId, phoneNumber, scanResult: scan.results, reportUrl: await scan.pdfUrl(), }), }); }); }, });

Three pieces are doing the work:

  • workflow<ScanRequest>({ id, execute }) is the loop. id is its name. event.body is the JSON you POST, typed as ScanRequest.
  • step.do saves the scan once FACIAL_SCAN.create finishes. A later replay does not create it again. The same is true of the notification step. Tools are on ctx, and ctx only exists inside the step.
  • step.waitForScan pauses the run. It resumes with a Scan handle when the patient finishes, or it throws after 30 minutes.

metadata is stored on the scan and comes back with the results, so the session already knows which patient it belongs to.

3. Allow the host

fetch only reaches hosts the loop allows. Open the loop’s Controls, add api.example.com under Allowed hosts, and save. See Controls.

4. Deploy

Click Deploy. The platform stores a new version, marks it active, and opens the loop overview. The next run uses that version. A run already in flight keeps the version it started with.

5. Trigger a run

From the UI: click Run and paste this body.

{ "patientId": "pat_18", "phoneNumber": "+15551234567" }

From the API: mint a token under Settings → API tokens, then POST to the loop. The URL is on the loop’s Access dialog.

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": "pat_18", "phoneNumber": "+15551234567" }'

The JSON body becomes event.body. The response includes a runId. Because this loop calls FACIAL_SCAN.create, it also includes a scanUrl you can send to the patient. The scan session exists before execute starts.

6. Read the run

Open the run from Runs. You’ll see the steps in order:

  • Generate a health scan — saved once create returns.
  • wait-for-scan — paused until the patient finishes, or until 30 minutes pass. The summary and the PDF are generated as part of it.
  • Send a notification — runs after the wait, with the results and the PDF url.

The run’s input is the POST body. Logs are every console.* from the loop.

Where to go next

Last updated on