Calling an external system
A loop calls another system with fetch: a practice system, a lab, a medical aid. Add the system’s host to the loop’s allowed hosts first. Any other host throws This loop cannot reach … Add it under Controls → Allowed hosts.
Keep credentials in variables and secrets, not in the code.
From a workflow
Put the call inside a step.do. The step saves what you return, and a replay uses that value instead of calling again.
import { workflow } from "@loops/sdk";
type Booking = { patientId: string; date: string };
export const bookFollowUpWorkflow = workflow<Booking>({
id: "book-follow-up",
execute: async ({ step, event, env }) => {
const { patientId, date } = event.body;
return step.do("Book the appointment", async () => {
const response = await fetch(
"https://api.practice.example.com/v1/appointments",
{
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `Bearer ${env.PRACTICE_API_KEY}`,
},
body: JSON.stringify({ patientId, date }),
},
);
if (!response.ok) {
throw new Error(`Booking failed: ${response.status}`);
}
return response.json();
});
},
});Throwing on a non-2xx response fails the step, and the platform retries it once.
From an agent
The model can’t call fetch itself. Wrap the call in a tool and pass the tool to submit. The model decides when to book, and execute makes the request with the input the model gave.
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),
},
);
if (!response.ok) {
throw new Error(`Booking failed: ${response.status}`);
}
return response.json();
},
});import { chatAgent, model } from "@loops/sdk";
import { bookAppointment } from "./tools";
export const healthChatAgent = chatAgent({
id: "health-chat",
execute: async ({ submit, messages }) =>
submit({
messages,
tools: [bookAppointment],
model: model("gpt-4o-mini"),
}),
});A tool’s execute gets only the model’s input. To use a secret, define the tool inside the agent’s execute, where env is in scope, or start a workflow from it. See Tools and Calling a workflow.