Skip to Content
Getting started

Getting started

Sign up

Go to loops.ollie.health/signup  and sign up with your work email or Google. Signing up creates your organization and makes you its owner.

Onboarding asks what you sell, your site or store, and which agent template to start from. Click Build agent and the template is deployed into your organization with your brand filled in. Every template is a real project you can open and change, and an agent that needs a workflow, such as a face scan, comes with that workflow already linked.

To start from scratch instead, create a loop from your loops list and pick New Workflow Loop or New Agent Loop.

The folder

A project is a folder of files. index.ts is the entry point. Add a file with the + next to the file list, and pick Workflow or Agent.

clinic/ index.ts agent consult-health-agent.ts agent health-scan.ts workflow tools.ts helpers, stored as a workflow

File types

Every file is an agent or a workflow. The platform reads the code when you deploy: a file that calls chatAgent() is an agent, and every other file is a workflow.

  • Agent — the file exports a chatAgent(). It answers messages. index.ts is an agent when the project is a conversation.
  • Workflow — the file exports a workflow(). It runs when triggered. index.ts is a workflow when the project is a run that waits.
  • Helpers — tools and other JavaScript that export neither. The file is stored as a workflow, but it has nothing to run. Other files import it.

When a file runs, the platform runs the first workflow() or chatAgent() it exports. Triggering a file that exports no workflow() throws Export a workflow({ id, execute }) from "@loops/sdk".

An agent entry point

index.ts calls chatAgent(), so the file is an agent:

import { chatAgent, model } from "@loops/sdk"; import { bookAppointment } from "./tools"; export const healthChatAgent = chatAgent({ id: "health-chat", execute: async ({ submit, messages }) => submit({ messages, instructions: "You are the front desk for a small clinic.", tools: [bookAppointment], model: model("gpt-4o-mini"), }), });

A workflow entry point

The same file can be a workflow instead. index.ts calls workflow(), so the file is a 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(), }), }); }); }, });

The type passed to workflow<ScanRequest> is the shape of event.body. Without it, event.body is unknown and the editor flags the destructuring.

One entry point. It is an agent or a workflow, not both.

Other files

An agent file is the same shape, in its own file. consult-health-agent.ts:

import { chatAgent, model } from "@loops/sdk"; export const consultHealthAgent = chatAgent({ id: "consult-health-agent", execute: async ({ submit, messages }) => submit({ messages, instructions: "You are a careful clinical triage assistant.", model: model("gpt-4o-mini"), }), });

A workflow file calls workflow(), same as a workflow entry point. Other files import it and start it with trigger.

A helper file holds tools or any other JavaScript. tools.ts:

import { defineTool, Type } from "@loops/sdk"; export const bookAppointment = defineTool({ name: "book_appointment", description: "Book a follow-up appointment at the practice", parameters: Type.Object({ patientId: Type.String(), date: Type.String(), }), execute: async (input) => { const response = await fetch( "https://api.practice.example.com/v1/appointments", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(input), }, ); return response.json(); }, });

How files see each other

import { healthScanWorkflow } from "./health-scan" loads the deployed version of that file. Deploying one file updates every file that imports it, from their next run or message. A file that throws when it loads breaks every file in the project, so keep top-level code to definitions.

Where to go next

Last updated on