API
Overview
The public API at https://dev.api.sparkwebinar.io/public/v1, by your organization's token.
Everything your organization has on the dashboard — its webinars, their sessions, registrations and ratings, its VSLs, the sales counted to them, its surveys and their responses, its pages, its webhooks — is read and, where it makes sense, written through the public API, so your own systems and the tools you wire together can work with it.
https://dev.api.sparkwebinar.io/public/v1Authentication
Section titled “Authentication”Every request carries your organization’s API token as a bearer:
Authorization: Bearer <token>The token is made on the dashboard, under Settings, by an owner of the organization. It is shown whole only once, as it is generated — copy it then: afterwards the dashboard shows only its first characters, to tell which one is in use. Lost, or leaked, generate a new one there; the old one stops working at once. The token names the organization, so no route does: everything you read and write is your organization’s, and anything else is answered as not found.
| Status | infoCode |
Why |
|---|---|---|
| 401 | UNAUTHENTICATED |
No bearer in the request, or a header not of that form. |
| 401 | API_TOKEN_INVALID |
A token that names no organization — generated anew since, or never ours. |
| 403 | ORGANIZATION_UNAVAILABLE |
The organization is not served — suspended, locked, or with no plan. |
| 403 | ORGANIZATION_BLACKLISTED |
The organization’s access to the API was blocked. Contact support. |
Call it from a server. There is no CORS on the public API: a page in a browser cannot call it, and should not — the token would be in the page.
Answers
Section titled “Answers”Every answer is JSON in the same envelope. The status is on the wire;
the body is data, and metadata where there is more to say.
{ "data": { "id": 12, "name": "Growth webinar", "..." : "..." } }A list answers a page of items, and in metadata.list the page it is
and how many there are in all:
{ "data": [ { "id": 41, "...": "..." }, { "id": 40, "...": "..." } ], "metadata": { "list": { "pagination": { "page": 1, "size": 20 }, "total": 57 } }}A failure is data: null, and an infoCode where there is one to give:
{ "data": null, "metadata": { "infoCode": "WEBINAR_NOT_FOUND" } }| Status | infoCode |
Why |
|---|---|---|
| 400 | VALIDATION_FAILED |
A body, a query string or an id that does not fit the route. For a body or a query string, metadata.details.issues says what (below). |
| 404 | <RESOURCE>_NOT_FOUND |
Nothing of your organization’s by that id — WEBINAR_NOT_FOUND, WEBINAR_REGISTRATION_NOT_FOUND, SURVEY_NOT_FOUND, SURVEY_RESPONSE_NOT_FOUND, CUSTOM_PAGE_NOT_FOUND, ORGANIZATION_WEBHOOK_NOT_FOUND, WEBINAR_TICKET_ORDER_NOT_FOUND, AB_TEST_NOT_FOUND, VSL_NOT_FOUND. |
| 409 | … |
The request is well-formed and cannot be done as things are; each route says its codes. |
| 429 | TOO_MANY_REQUESTS |
Over the limit below. |
A body or a query string that does not fit names each field that was
wrong, the first twenty at most: path is its keys joined by dots, empty
for the whole body, and message what was wrong with it, in English. An id
in the path that is not one answers VALIDATION_FAILED alone.
{ "data": null, "metadata": { "infoCode": "VALIDATION_FAILED", "details": { "issues": [{ "path": "email", "message": "Invalid email address" }] } }}A route that deletes answers 200 with data: null.
A list takes its page in the query string: page, from 1, and size, up
to 100 — twenty by default. Its filters go beside them, each a query
parameter named after the field it narrows by; a filter not sent is not
applied. Text filters such as a name or an e-mail match inside the
value, whatever the case. An instant is filtered as a span: …From is on
or after it, …To before it, either alone or both. Every list is newest
first, but a webinar’s sessions, which are in the order they start.
/public/v1/webinar-registrations?webinarId=12&createdAtFrom=2026-10-01T00:00:00Z&size=100&page=2Ids and instants
Section titled “Ids and instants”Every id is a whole number. Every instant is an ISO 8601 string in UTC,
2026-11-03T22:00:00.000Z, and is sent the same way — with an offset,
if not Z.
Rate limit
Section titled “Rate limit”Every organization may make 120 requests a minute, over the whole public API, whatever its plan. Each answer says where you stand:
| Header | What it says |
|---|---|
X-RateLimit-Limit |
The limit, 120. |
X-RateLimit-Remaining |
How many are left in this minute. |
X-RateLimit-Reset |
Seconds until the minute turns over. |
Over it, the answer is 429 with TOO_MANY_REQUESTS and a Retry-After
in seconds. Reading a list in pages of 100 stays well under it.
One address may also make 600 requests a minute, whatever tokens they
carry, counted before the token is checked. Over that it is the same 429.