Browse the docs

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 are https://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:

bash
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 JWT

More keys: dashboard → project → API keys (POST /dashboard/projects/{projectId}/api-keys with { "name", "mode": "test" | "live" }). See authentication.md.

bash
export LATIDO_API_KEY=lat_test_…
npm install @latido/sdk

2. 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.

ts
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_…
bash
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

ts
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",
  );
}
bash
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:

bash
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):

ts
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 live key and set up each provider (dashboard → Providers) with Latido's managed app where available, or your own client id and secret.