API Access
Programmatically access your event data
Set up with AI in seconds
Copy a ready-made prompt and paste it into Lovable, Cursor, Replit, ChatGPT, or Claude to scaffold your integration.
Getting an API Key
- Go to Admin Dashboard → Settings
- Scroll to the API Keys section
- Click "Generate New Key" and give it a descriptive name
- Copy the generated key immediately — it is only shown once
Authentication
All requests must include your API key in the x-api-key header:
curl -H "x-api-key: knmi_your_key_here" \
"https://hbyaeoqwllejvvcajxkd.supabase.co/functions/v1/event-data-api?resource=event"Base URL
https://hbyaeoqwllejvvcajxkd.supabase.co/functions/v1/event-data-apiAvailable Resources
Each API key is scoped to your organization. By default, every resource returns data across all events in your org. Add the optional event_id filter only when you want to narrow to a single event.
event
All events in your organization (or one event when event_id is passed).
GET ?resource=eventFilter to one event:GET ?resource=event&event_id=YOUR_EVENT_IDid, event_name, event_date, event_end_date, venue_name, venue_city, timezone, max_attendees, is_public
participants
All registered participants across every event in your org.
GET ?resource=participantsFilter to one event:GET ?resource=participants&event_id=YOUR_EVENT_IDid, full_name, email, phone, company, job_title, status, ticket_type, event_id, group_assignment, track_assignment, created_at, updated_at
sessions
All sessions across every event in your org.
GET ?resource=sessionsFilter to one event:GET ?resource=sessions&event_id=YOUR_EVENT_IDid, title, description, start_time, end_time, location, track, zone, status, session_code, event_id, event_day_id, max_participants, current_participants
tickets
All ticket types across every event in your org.
GET ?resource=ticketsFilter to one event:GET ?resource=tickets&event_id=YOUR_EVENT_IDid, name, price, currency, quantity_total, quantity_sold, is_active, event_id
checkins
All check-in / admission tickets across every event in your org.
GET ?resource=checkinsFilter to one event:GET ?resource=checkins&event_id=YOUR_EVENT_IDid, full_name, email, ticket_number, checked_in, checked_in_at, status, event_id
Response Format
All responses are JSON with the following structure:
{
"data": [...],
"resource": "participants",
"organization_id": "uuid",
"pagination": {
"limit": 1000,
"offset": 0,
"count": 1000,
"has_more": true,
"next_offset": 1000
}
}Lists return at most 1000 rows per request, oldest first. Add &limit= (1 to 1000) and &offset=, and keep requesting next_offset while has_more is true.
JavaScript Example
const API_KEY = "knmi_your_key_here";
const BASE_URL = "https://hbyaeoqwllejvvcajxkd.supabase.co/functions/v1/event-data-api";
// Fetches every page of a resource (lists return up to 1000 rows per call).
async function getAll(resource, eventId) {
const rows = [];
let offset = 0;
while (true) {
const params = new URLSearchParams({ resource, limit: "1000", offset: String(offset) });
if (eventId) params.set("event_id", eventId);
const response = await fetch(`${BASE_URL}?${params}`, {
headers: { "x-api-key": API_KEY },
});
if (response.status === 429) {
const wait = Number(response.headers.get("Retry-After") ?? "60");
await new Promise((r) => setTimeout(r, wait * 1000));
continue;
}
if (!response.ok) throw new Error(`API error: ${response.status}`);
const { data, pagination } = await response.json();
rows.push(...data);
if (!pagination?.has_more) return rows;
offset = pagination.next_offset;
}
}
// Usage
const participants = await getAll("participants", "your-event-id");
console.log(`Found ${participants.length} participants`);Error Codes
| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 400 | Missing or unknown resource parameter |
| 405 | HTTP method not allowed (use GET) |
| 500 | Internal server error |
Webhooks
Webhooks let you receive real-time HTTP POST notifications when events happen in your organization.
Setup
- Go to Admin Dashboard → Settings → Webhooks
- Click "Add Webhook"
- Enter your endpoint URL and select which events to subscribe to
- A signing secret is generated automatically — use it to verify payloads
Available Events
| Event | Description |
|---|---|
| participant.created | New participant registered |
| participant.approved | Participant approved |
| ticket.purchased | Ticket purchased |
| checkin.completed | Attendee checked in |
| session.created | Session created |
| session.updated | Session updated |
| application.submitted | Application submitted |
| application.reviewed | Application reviewed |
Payload Format
{
"id": "5f0c6f0e-…", // stable across retries: de-duplicate on it
"event": "participant.created",
"timestamp": "2026-04-15T12:00:00.000Z",
"organization_id": "uuid",
"data": {
"participant_id": "uuid",
"event_id": "uuid",
"full_name": "Jane Doe",
"email": "jane@example.com",
"status": "pending"
}
}Delivery and Retries
- Endpoints must use
https://and a public host name or address. Private, loopback and internal addresses are refused. - Respond with any
2xxstatus within 10 seconds. Redirects are not followed and count as a failed attempt. - Failed deliveries are retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours, 12 hours and 24 hours, then marked failed.
- Delivery is at-least-once, so the same event can arrive twice. Use the
idfield (also sent as theX-Webhook-Idheader) to ignore duplicates. - Events are usually delivered within a minute or two of the change. Order is not guaranteed.
Verifying Signatures
Each webhook request includes an X-Webhook-Signature header — an HMAC-SHA256 hex digest of the request body using your signing secret.
import crypto from "crypto";
function verifySignature(body, signature, secret) {
const expected = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
const a = Buffer.from(signature ?? "", "utf8");
const b = Buffer.from(expected, "utf8");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
// Verify against the raw request body, before JSON parsing.Rate Limiting
The API enforces a rate limit of 100 requests per minute per API key. Every response includes rate limit headers:
X-RateLimit-Limit: 100 X-RateLimit-Remaining: 97 X-RateLimit-Reset: 1713200160
X-RateLimit-Limit— Maximum requests allowed per windowX-RateLimit-Remaining— Requests remaining in the current windowX-RateLimit-Reset— Unix timestamp when the window resets
If you exceed the limit, you'll receive a 429 Too Many Requests response with a Retry-After header indicating how many seconds to wait.
Best Practices
- Use the
event_idfilter to reduce response size when you only need data for one event - Cache responses where appropriate to minimize API calls
- Respect rate limit headers — back off when
X-RateLimit-Remainingis low - Store your API key in environment variables, not in source code
- Create separate keys for different integrations so you can revoke them independently
- Monitor the "Last used" timestamp in your admin settings to track key usage