Skip to content

API Reference

StoryboardCanvas API

Reference for the public and signed-in HTTP endpoints currently shipped in production. Six endpoints, exact request and response shapes, error codes, and copy-paste curl examples. A broader developer API for projects, scripts, and breakdowns is on the roadmap.

Conventions

Base URL

All endpoints below are served from https://www.storyboardcanvas.ai. There is no separate api. subdomain — everything lives under the same Next.js app.

Authentication

Public endpoints accept anonymous requests. Endpoints marked Bearer require a signed-in Clerk session — most clients carry it as a session cookie set by the sign-in flow. There is no separate API key tier today; programmatic access is on the waitlist.

Content type

All POST endpoints accept application/json. Responses are always JSON unless explicitly noted (the health probe returns JSON too).

Errors

Errors return a JSON body with at least { error, code }. Some routes also return a detail string and issues[] array of Zod parse errors. The HTTP status mirrors the failure category — 400 for bad input, 401 for missing auth, 429 for rate limits, 5xx for server faults.

Endpoints

GET/api/healthPublic

Liveness probe

Cheap, dependency-free liveness check used by uptime monitoring and status pages. No DB, no auth, no downstream probes. Cached at the edge for 10 seconds via Cache-Control: public, max-age=10, s-maxage=10 — calling more often than that returns the same payload.

Response (200)
{
  "ok": true,
  "version": "a1b2c3d",        // first 7 chars of the deployed commit SHA
  "region": "iad1",            // serving region or "unknown"
  "timestamp": "2026-04-26T10:15:00.000Z"
}
Errors
StatusCodeMeaning
200Always 200 when the lambda is reachable. A non-200 means the platform itself is down (cold-start failure, network).
Example
curl https://www.storyboardcanvas.ai/api/health
POST/api/waitlistPublic

Waitlist signup

Adds an email to the closed-beta waitlist. Idempotent — submitting the same email twice returns the existing row including the personal referral code. If a Clerk session is present we attach the user id so the gate page can find the user later. Rate-limited per IP to discourage spam.

Request body
{
  "email":        "alex@example.com",   // required
  "name":         "Alex Chen",          // optional
  "role":         "director",           // optional: director, producer, writer,
                                        //           dp, ad, student, other
  "project_type": "feature",            // optional: feature, short, series,
                                        //           commercial, music_video,
                                        //           student, documentary, other
  "job_title":    "Freelance director", // optional
  "company":      "Northwind Films",    // optional
  "referrer":     "instagram",          // optional, free-text
  "referred_by":  "ABCD2345"            // optional, another user's referral_code
}
Response (200)
{
  "ok": true,
  "id": "uuid",
  "referral_code": "K9JM3PQX",
  "referral_count": 0,
  "priority_score": 100,
  "duplicate": false
}
Errors
StatusCodeMeaning
400bad-bodyMissing or malformed email, or any field exceeds its length cap.
429rate-limitedToo many submissions from this IP. Includes a Retry-After header.
500server-errorDatabase write failed. Safe to retry.
Example
curl -X POST https://www.storyboardcanvas.ai/api/waitlist \
  -H "Content-Type: application/json" \
  -d '{"email":"alex@example.com","role":"director"}'
POST/api/contactPublic

Contact form

Sends a contact form submission to the support inbox via Resend. Used by the /contact page. Per-IP rate-limited to 5 requests per minute. All inputs are HTML-escaped before being placed in the outbound email body.

Request body
{
  "name":    "Alex Chen",                       // required, 1-200 chars
  "email":   "alex@example.com",                // required, valid email
  "subject": "Question about the Studio plan",  // required, 1-500 chars
  "message": "Hi - I had a few questions..."    // required, 1-10000 chars
}
Response (200)
{ "success": true }
Errors
StatusCodeMeaning
400bad-bodyA required field is missing, or a field exceeds its length cap.
429rate-limitedMore than 5 submissions from this IP in the last minute. Includes a Retry-After header.
500server-errorEmail transport failed. The form retries safely.
Example
curl -X POST https://www.storyboardcanvas.ai/api/contact \
  -H "Content-Type: application/json" \
  -d '{"name":"Alex","email":"alex@example.com","subject":"Hello","message":"Hi there"}'
POST/api/billing/checkoutBearer

Start a Stripe Checkout session

Creates a Stripe Checkout Session for the signed-in user and returns the hosted-checkout URL. The client_reference_id is the Supabase owner uuid so the billing webhook can settle the subscription without an extra Clerk round-trip. If a prior Stripe customer exists for this user we reuse the customer id (no duplicate customers, no double trials).

Request body
{
  "plan":     "solo" | "team" | "studio" | "agency" | "network" | "broadcaster",
  "interval": "month" | "year"   // optional, defaults to "month"
}
Response (200)
{
  "url": "https://checkout.stripe.com/c/pay/cs_live_..."
}
Errors
StatusCodeMeaning
401unauthorizedNo valid Clerk session on the request.
400bad-bodyPlan is missing or not one of the accepted enum values.
503stripe_not_configuredBilling is not enabled in this environment. The /billing UI surfaces this as a toast.
500server-errorStripe API call failed. Safe to retry after a backoff.
Example
curl -X POST https://www.storyboardcanvas.ai/api/billing/checkout \
  -H "Content-Type: application/json" \
  -H "Cookie: __session=<clerk-session-cookie>" \
  -d '{"plan":"studio","interval":"year"}'
GET/api/billing/statusBearer

Read subscription state

Returns the signed-in user's current plan, status, daily credit allowance, and Stripe customer id. Reads from Supabase (what the webhook persisted) rather than Clerk publicMetadata so the response always reflects reality. Soft-fails to a free / none payload on any error so the UI never spinner-loops.

Response (200)
{
  "plan":               "free" | "solo" | "team" | "studio" |
                        "agency" | "network" | "broadcaster",
  "status":             "active" | "trialing" | "past_due" |
                        "canceled" | "none",
  "credits_daily":      number,
  "current_period_end": "2026-05-26T00:00:00Z" | null,
  "stripe_customer_id": "cus_..." | null,
  "has_subscription":   boolean
}
Errors
StatusCodeMeaning
401unauthorizedNo valid Clerk session on the request.
200On any internal error the route returns a free / none payload with status 200 so the UI keeps rendering.
Example
curl https://www.storyboardcanvas.ai/api/billing/status \
  -H "Cookie: __session=<clerk-session-cookie>"
POST/api/account/delete-requestBearer

Request account deletion (GDPR)

Files a pending deletion request for the signed-in user. We do not auto-delete on click — an admin processes the request manually after Stripe settles. The user must type their primary email back to prove intent (case-insensitive match against Clerk's email-of-record).

Request body
{
  "confirmEmail": "alex@example.com",         // required, must equal account email
  "reason":       "Wrapping up production"    // optional, <=2000 chars
}
Response (200)
{
  "ok": true,
  "saved": true,
  "request_id": "uuid",
  "submitted_at": "2026-04-26T10:15:00.000Z"
}
Errors
StatusCodeMeaning
401unauthorizedNo valid Clerk session on the request.
400bad-bodyMissing confirmEmail or reason exceeds the 2000-char cap.
400email-mismatchconfirmEmail does not match the account's primary email.
200table_missingReturned with { saved: false, table_missing: true } when the deletion table hasn't been provisioned in this environment.
500db-errorDatabase write failed. Safe to retry.
Example
curl -X POST https://www.storyboardcanvas.ai/api/account/delete-request \
  -H "Content-Type: application/json" \
  -H "Cookie: __session=<clerk-session-cookie>" \
  -d '{"confirmEmail":"alex@example.com","reason":"Wrapping production"}'

Need an endpoint that's not listed?

A broader developer API covering projects, scripts, breakdowns, shot lists, characters, and scheduling is on the post-launch roadmap. Tell us what you'd build via the contact form and we'll factor your use case into the v1 surface.

Public release June 2026 — now accepting payment · waitlist is free

Private beta is rolling out — ~20% off via annual billing

Join the growing waitlist. No payment today — discount activates at launch.

Join the waitlist