API reference
Quickstart: from zero to a signed webhook in 5 minutes
You will get a test API key, connect a member to the Latido Demo provider (a simulated wearable with synthetic data), read their workouts and receive a signed webhook. Nothing here needs a real Garmin or Strava app: test keys only ever see the demo provider.
URLs. The API is
https://api.getlatido.com; the dashboard and the hosted connect page arehttps://app.getlatido.com.
1. Get a test key
Sign up in the dashboard. Sign-up creates your account, a first project and a test API key, shown once — copy it. Or from a terminal:
curl -s https://api.getlatido.com/auth/sign-up \
-H 'Content-Type: application/json' \
-d '{"email":"ada@example.com","password":"a-long-password","name":"Ada","accountName":"Acme Health"}'
# → data.apiKey.secret = "lat_test_…" (store it), data.token = dashboard session JWTMore keys: dashboard → project → API keys (POST /dashboard/projects/{projectId}/api-keys
with { "name", "mode": "test" | "live" }). See authentication.md.
export LATIDO_API_KEY=lat_test_…
npm install @latido/sdk2. Create a connect session
A connect session is a one-hour link to Latido's hosted page, where your user picks a
provider and authorizes it. The member is created on the way if externalUserId is new.
import { Latido } from "@latido/sdk";
const latido = new Latido({ apiKey: process.env.LATIDO_API_KEY! });
const { data: session } = await latido.connect.sessions.create({
externalUserId: "user-42", // your id for this person
redirectUrl: "http://localhost:3000/done", // test keys accept http://localhost on any port
});
console.log(session.url, session.memberId); // https://app.getlatido.com/connect/cst_…, mem_…curl -s https://api.getlatido.com/v1/connect/sessions \
-H "Authorization: Bearer $LATIDO_API_KEY" -H 'Content-Type: application/json' \
-d '{"externalUserId":"user-42","redirectUrl":"http://localhost:3000/done"}'For a non-localhost redirectUrl, add its origin to the project's allowed redirect
origins first (dashboard → project settings), or the call fails with
REDIRECT_URL_NOT_ALLOWED.
3. Connect the demo provider
Open session.url. The hosted page lists Latido Demo; pick it and you land on a fake
consent screen ("Latido Demo wants to share your data"). Allow sends the browser back to:
http://localhost:3000/done?status=connected&provider=latido_demo&connectionId=conn_…(Deny comes back with status=error&provider=latido_demo&reason=access_denied.) Parse it
with latido.connect.parseRedirect(url) — see connect-flow.md.
The connection is now active and a backfill runs in the background: the demo provider
generates realistic workouts, daily summaries, sleep, body measurements and heart rate for
the project's defaultBackfillDays (30 by default; the demo caps it at 90).
4. Read the workouts
const memberId = session.memberId;
const { data: member } = await latido.members.get(memberId);
console.log(member.connections[0]?.backfill.status, member.recordCounts);
for await (const workout of latido.members.workouts.iterate(memberId, { from: "2026-10-01" })) {
console.log(
workout.startTime, workout.sport,
workout.distanceMeters, "m", workout.durationSeconds, "s",
workout.averageHeartRateBpm, "bpm",
);
}curl -s "https://api.getlatido.com/v1/members/$MEMBER_ID/workouts?from=2026-10-01&limit=10" \
-H "Authorization: Bearer $LATIDO_API_KEY"Units never change per provider: meters, seconds, bpm, kcal, watts, m/s. Fields a provider
does not give are absent, never 0. See data-model.md.
5. Receive a webhook
Webhook endpoints are managed in the dashboard (project → Webhooks), not with an API key:
create one for test mode with your URL and the event types you want (none = all). The
signing secret (whsec_…) is shown once. The same over HTTP, with the session JWT:
curl -s https://api.getlatido.com/dashboard/projects/$PROJECT_ID/webhook-endpoints \
-H "Authorization: Bearer $SESSION_JWT" -H 'Content-Type: application/json' \
-d '{"mode":"test","url":"https://your-tunnel.example/latido","eventTypes":["workout.created","connection.created"]}'Your handler (any framework that gives you the raw body):
import { Latido, LatidoWebhookVerificationError } from "@latido/sdk";
export async function POST(request: Request) {
try {
const event = await Latido.webhooks.verify(
await request.text(), // the raw body, untouched
request.headers.get("Latido-Signature"),
process.env.LATIDO_WEBHOOK_SECRET!,
);
console.log(event.type, event.data);
return new Response(null, { status: 204 });
} catch (error) {
if (error instanceof LatidoWebhookVerificationError) return new Response(null, { status: 400 });
throw error;
}
}Trigger traffic: the endpoint's Send test event button (webhook.test), or the
dashboard's demo button (POST /dashboard/projects/{projectId}/demo/simulate, body
{ "externalUserId"?: "user-42" }, default demo-athlete): it makes sure that test member has
a demo connection, then pushes a new demo workout through the normal provider path — stored,
normalized, workout.created delivered.
Deliveries come from Latido's servers: the URL must be reachable from them (a tunnel such as
ngrok or cloudflared for a local handler; Latido cannot reach your localhost). Live
endpoints must be https and public.
Next
- connect-flow.md — the hosted page, redirect parameters, errors
- webhooks.md — every event, payloads, retries, verification in Python/Node
- mcp.md — let Claude Code or Cursor read the data
- errors-and-limits.md — error codes, plan limits, rate limits
- Going live: create a
livekey and set up each provider (dashboard → Providers) with Latido's managed app where available, or your own client id and secret.