Skip to main content
bthebigclass
v1 — Public REST API

An honest API for creators.

Most creator platforms either don't ship an API or hide it behind a sales call. Ours is right here: scoped tokens, transparent rate limits, versioned endpoints, and docs you can ctrl-F through. Generate a key in your dashboard and you're integrating in five minutes.

What you get

Scoped bearer tokens

Every key is scoped: read:courses, read:students, write:enrollments, etc. Grant only what the integration needs. Revocable anytime, no support ticket.

Predictable rate limits

60 req/min, 1,000 req/day per key on every tier. Every response carries X-RateLimit-* headers so your client can throttle proactively.

Versioned + stable

All endpoints live under /api/v1/. Breaking changes ship under /v2 with a 6-month deprecation window for v1. You won't wake up to a broken integration.

Standard envelopes

Lists return { data, pagination }. Errors return { error: { code, message } }. Cursors not page numbers. JSON only — no XML legacy.

Open source on the wedge

Our data-export tooling, webhook receivers, and these endpoint contracts are MIT-licensed. Fork the contract; you don't have to fork the platform.

One-time secret reveal

The full secret is shown exactly once on creation — same pattern Stripe and GitHub use. We never store plaintext; lose it and you revoke + reissue.

Quickstart

  1. 1

    Generate a key

    Open /dashboard/developer in your workspace and click New API key. Pick scopes, name the key, hit Generate. Copy the secret immediately — you only see it once.
  2. 2

    Make your first call

    Pass the key as a bearer token. Example:
    curl https://thebigclass.com/api/v1/courses \
      -H "Authorization: Bearer tbc_YOUR_SECRET_HERE"
  3. 3

    Read the rate-limit headers

    Every response includes X-RateLimit-Remaining and X-RateLimit-Reset. Back off when you see < 5 remaining; otherwise you'll trip a 429 with aRetry-After header.
  4. 4

    Production checklist

    Store secrets in a real secret manager (1Password, Vault, AWS SM). Rotate every 90 days. Never embed a key in client-side bundles or mobile apps — those should proxy through your backend, which holds the key.

Endpoints

Every endpoint enforces its listed scope. Methods are case-sensitive.

MethodPathScopeStatus
GET/api/v1/courses

List published courses with pagination.

read:coursesLive
GET/api/v1/courses/{id}

Fetch a single course, including modules + lessons metadata.

read:coursesLive
GET/api/v1/students

List students with progress + enrolment history.

read:studentsLive
POST/api/v1/students

Create a student. Used by external CRMs syncing leads.

write:studentsLive
GET/api/v1/orders

Receipt + entitlement history for analytics dashboards.

read:ordersLive
POST/api/v1/enrollments

Enrol a student in a course. Idempotent on (studentId, courseId).

write:enrollmentsLive
GET/api/v1/analytics/summary

Aggregate revenue + completion metrics over a window.

read:analyticsLive

Webhooks

We POST a signed JSON payload to your URL every time something happens in your workspace — student created, order paid, recording ready, etc. Subscribe in /dashboard/developer/webhooks.

Events we send

Pick the events you care about, paste your URL, get a signing secret. Every delivery carries an X-TBC-Signature HMAC-SHA256 header we recommend you verify.

Events

  • student.created
  • student.updated
  • student.deleted
  • enrollment.created
  • enrollment.revoked
  • order.paid
  • order.refunded
  • course.published
  • course.archived
  • live_session.started
  • live_session.ended
  • recording.ready
  • certificate.issued

Delivery guarantees

  • · At-least-once. Same event id may arrive twice — dedupe on the id field.
  • · Retries with exponential backoff (1m → 5m → 30m → 2h → 6h → 24h) for 5xx + network errors.
  • · Delivery log retained for 30 days at /dashboard/developer/webhooks.
  • · Endpoints failing for 7 consecutive days are auto-disabled; you get an email.
POST https://your-app.com/webhooks/tbc HTTP/1.1
Content-Type: application/json
X-TBC-Signature: t=1716102400,v1=8a92…
X-TBC-Event:     order.paid
X-TBC-Delivery:  evd_2k9f…

{
  "id": "evt_01HXYZ…",
  "type": "order.paid",
  "created_at": "2026-05-19T14:23:14Z",
  "workspace_id": "org_…",
  "data": { /* order object */ }
}

A few things creators build with these events:

  • CRM sync. Subscribe to student.created and enrollment.created — every new student is auto-pushed to HubSpot / Zoho / a Google Sheet, with their course list tagged.
  • Slack / Discord pings. Subscribe to order.paid — your team gets a Slack message every time someone buys, including the product name and customer email.
  • Accounting export. Subscribe to order.paid + order.refunded — your bookkeeping tool (Tally / QuickBooks / Zoho Books) gets each transaction with gross / gateway-fee / net columns pre-split.
  • Auto-issue downloads. Subscribe to order.paid for a digital product — your serverless function generates a watermarked PDF and emails it to the buyer in seconds.
  • Recording archival. Subscribe to recording.ready — when a live class finishes, your script copies the file to your own S3 bucket for long-term storage.

Webhooks are the right tool when you need to react in near-real-time. For batch / periodic sync, prefer the REST API above with cursor pagination.

Verifying our signature (Node.js example)

import crypto from 'crypto'

const SECRET = process.env.TBC_WEBHOOK_SECRET!
app.post('/webhooks/tbc', express.raw({ type: '*/*' }), (req, res) => {
  const sig = req.header('X-TBC-Signature') || ''
  // X-TBC-Signature: t=<unix>,v1=<hex>
  const parts = Object.fromEntries(sig.split(',').map(p => p.split('=')))
  const ts = Number(parts.t)
  if (Math.abs(Date.now() / 1000 - ts) > 300) return res.status(400).end() // 5-min replay window
  const expected = crypto
    .createHmac('sha256', SECRET)
    .update(`${ts}.${req.body.toString('utf8')}`)
    .digest('hex')
  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1))) {
    return res.status(401).end()
  }
  // Trust the body now — your job to dedupe on body.id
  const event = JSON.parse(req.body.toString('utf8'))
  /* … */
  res.json({ ok: true })
})

Use express.raw (or your framework's equivalent) so the body bytes match what we signed. Parsing JSON BEFORE verifying will silently change whitespace and break the HMAC.

Rate limits

Per key

60 / min

Resets at the top of each minute, aligned to clock minutes.

Per key

1,000 / day

Resets at 00:00 UTC. Need more? Enterprise plans get custom quotas.

Response headers (always present)

  • X-RateLimit-Limit — your per-minute cap.
  • X-RateLimit-Remaining — calls left this minute.
  • X-RateLimit-Reset — unix-seconds when the per-minute bucket resets.
  • X-RateLimit-Daily-Limit, …-Daily-Remaining, …-Daily-Reset — same shape for the daily bucket.
  • Retry-After — seconds to wait, only on 429 responses.

Errors

Every error response uses the same envelope so generic clients can branch on.error.code without parsing prose.

{
  "error": {
    "code": "rate_limited",
    "message": "Per-minute rate limit reached. Retry after 23s.",
    "retryAfterSeconds": 23
  }
}
  • unauthorized — 401 — missing or malformed bearer token.
  • forbidden — 403 — token exists but lacks the required scope.
  • not_found — 404 — id doesn't resolve in this workspace.
  • rate_limited — 429 — per-minute or per-day bucket exhausted.
  • invalid_request — 422 — body / params failed validation.
  • internal_error — 500 — our fault. Retry with exponential backoff.

Ship something today.

Generate a key, hit your first endpoint in five minutes. If something feels missing or wrong, tell us — every endpoint here was built because a creator asked for it.