API documentation
A REST API over your own Loopinglive data. Everything is JSON, everything is authenticated with a bearer token, and every list is paginated.
Base URL
https://loopinglive.com/api/public/v1Authentication
Send your key in the Authorization header. Create one under Settings → API keys. Keys are shown once at creation — we store only a hash, so a lost key has to be revoked and replaced.
curl https://loopinglive.com/api/public/v1/webinars \
-H "Authorization: Bearer ll_live_your_key_here"Rate limits
100 requests per minute per key. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Exceeding the limit returns 429 with a Retry-After header.
Endpoints
/webinarsLists the webinars belonging to the key's owner.
| Parameter | Type | Required | Description |
|---|---|---|---|
| status | string | no | draft, published, or all (default) |
| page | integer | no | 1-based page number |
| limit | integer | no | 1–100, default 25 |
curl https://loopinglive.com/api/public/v1/webinars \
-H "Authorization: Bearer ll_live_your_key_here"Response
{
"webinars": [
{
"id": "0f8c…",
"title": "The 3-Offer Framework",
"status": "published",
"video_duration_seconds": 2880,
"created_at": "2026-08-30T12:00:00Z"
}
],
"total": 1,
"page": 1,
"limit": 25
}/webinars/{id}One webinar, with its recent sessions and registrant count.
curl https://loopinglive.com/api/public/v1/webinars/{id} \
-H "Authorization: Bearer ll_live_your_key_here"Response
{
"webinar": {
"id": "0f8c…",
"title": "The 3-Offer Framework",
"status": "published",
"registrantCount": 128,
"sessions": [
{ "id": "a1b2…", "starts_at": "2026-09-04T19:00:00Z", "status": "scheduled" }
]
}
}/webinars/{id}/registrantsRegistrants for one webinar.
| Parameter | Type | Required | Description |
|---|---|---|---|
| segment | string | no | Filter by watch-depth segment |
| boughtOnly | boolean | no | Only people who bought |
| page | integer | no | 1-based page number |
| limit | integer | no | 1–100, default 25 |
curl https://loopinglive.com/api/public/v1/webinars/{id}/registrants \
-H "Authorization: Bearer ll_live_your_key_here"Response
{
"registrants": [
{
"id": "9c3d…",
"full_name": "Sarah Okonkwo",
"email": "sarah@example.com",
"attended": true,
"watch_percentage": 63,
"bought": false
}
],
"total": 128,
"page": 1,
"limit": 25
}/webinars/{id}/registrantsRegisters someone programmatically.
| Parameter | Type | Required | Description |
|---|---|---|---|
| full_name | string | yes | 2–100 characters |
| string | yes | A valid email address | |
| phone | string | no | Digits, spaces, + ( ) - |
| country_code | string | no | ISO 3166-1 alpha-2 |
| session_id | uuid | no | Defaults to the next session |
curl -X POST https://loopinglive.com/api/public/v1/webinars/{id}/registrants \
-H "Authorization: Bearer ll_live_your_key_here"Response
{
"registrant": { "id": "9c3d…", "email": "sarah@example.com" }
}/sessionsSessions across every webinar the key can see.
| Parameter | Type | Required | Description |
|---|---|---|---|
| webinar_id | uuid | no | Restrict to one webinar |
| status | string | no | scheduled, live, or ended |
curl https://loopinglive.com/api/public/v1/sessions \
-H "Authorization: Bearer ll_live_your_key_here"Response
{
"sessions": [
{ "id": "a1b2…", "webinar_id": "0f8c…", "starts_at": "2026-09-04T19:00:00Z", "status": "scheduled" }
],
"total": 12,
"page": 1,
"limit": 25
}Errors
| 400 | The request was malformed — usually a missing parameter. |
| 401 | The API key is missing, invalid, revoked or expired. |
| 403 | The account is suspended. |
| 404 | No such resource, or it belongs to someone else. |
| 422 | The body failed validation. The response lists each issue. |
| 429 | Rate limit exceeded. Retry-After says how long to wait. |
| 500 | Something went wrong on our side. |
Webhooks
Loopinglive can POST to a URL of yours whenever something happens. Configure endpoints under Settings → Webhooks. Failed deliveries retry five times with increasing backoff — 5 minutes, 30 minutes, 2 hours, then 8 hours.
| registrant.created | Someone registers |
| registrant.attended | Someone joins the room |
| registrant.completed | Someone watches 90% or more |
| registrant.clicked_offer | Someone clicks the offer |
| registrant.bought | Someone is marked as bought |
| registrant.no_show | Someone registered but never showed |
| session.started | A session goes live |
| session.ended | A session finishes |
Payload
{
"event": "registrant.created",
"timestamp": "2026-09-03T09:14:22.000Z",
"data": {
"registrantId": "9c3d…",
"name": "Sarah Okonkwo",
"email": "sarah@example.com",
"webinarTitle": "The 3-Offer Framework",
"registeredAt": "2026-09-03T09:14:21.880Z"
}
}Verifying the signature
Every request carries X-Loopinglive-Signature: an HMAC-SHA256 of the raw request body, keyed with your endpoint’s signing secret. Hash the bytes you received — re-serialising the parsed JSON can reorder keys and produce a different digest.
import { createHmac, timingSafeEqual } from "node:crypto";
export function verify(rawBody, signatureHeader, secret) {
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected);
const b = Buffer.from(signatureHeader);
return a.length === b.length && timingSafeEqual(a, b);
}SDKs
None yet. The API is plain REST with bearer auth, so any HTTP client works.