# Hep.gg Forms - Public Form API Build forms in the dashboard, publish them, and collect submissions. Forms are built and published in the Hep.gg dashboard; there is no public endpoint to create or edit a form. The endpoints below are the PUBLIC surface a published form exposes so a client can render it and submit to it. Base URL: https://forms.hep.gg A published form renders at https://forms.hep.gg/; its JSON API lives under https://forms.hep.gg/api/v1/forms/public/. Most endpoints are unauthenticated; "hepgg"/"discord" login modes use the visitor's Hep.gg session cookie. ## Endpoints ### GET /api/v1/forms/public/:slug Fetch a published form's definition (pages, fields, login modes, theme). The owner's identity and the delivery actions are stripped from this payload. 404 if the slug is unknown or the form is not active. Increments the view counter. Response: { "ok": true, "data": { "form": { ...public form... } } } ### POST /api/v1/forms/public/:slug/submit Submit the form. Body (JSON): data object required. Map of field_key -> value, validated server-side against the form's fields. turnstileToken string Cloudflare Turnstile token. Required for ANONYMOUS submitters only (no Hep.gg session AND no smsPinToken). smsPinToken string From the SMS-PIN handshake below (sms_pin forms). smsPinPhone string The verified phone (same value as smsPinToken). Login modes are set per form (any of: anonymous, hepgg, discord, sms_pin). A submission must satisfy one the form allows: - anonymous : open to anyone; captcha enforced. - hepgg / discord : requires a signed-in Hep.gg session cookie. - sms_pin : requires a verified smsPinToken (see the handshake below). Responses: 200 { "ok": true, "data": { "submissionId": "" } } 400 validation failed: { "ok": false, "error": "...", "fieldErrors": { "": "" } } 400 captcha verification failed. 401 the form requires sign-in and the caller is not signed in. 403 form not open yet / closed / submission cap reached. 429 per-IP rate limit reached (honor Retry-After, seconds). ### POST /api/v1/forms/public/:slug/partial Save-and-continue: store partial answers and get a token to resume later. Only works when the form has save-and-continue enabled (otherwise 400). Body: { data: object, currentPageId?: string, partialId?: string } Pass a prior partialId to overwrite that row instead of creating a new one. Response: { "ok": true, "data": { "partialId": "", "expiresAt": "" } } Partials expire after 14 days. ### GET /api/v1/forms/public/:slug/partial/:partialId Restore a saved partial (its data + current page) to resume the form. ### POST /api/v1/forms/public/:slug/sms/start Begin SMS-PIN verification for sms_pin forms. Sends a 6-digit code to the phone. Body: { phone, turnstileToken } phone matches ^\+?[0-9]{7,16}$ (full international format, e.g. +14155550101). Rate limited per IP / phone. ### POST /api/v1/forms/public/:slug/sms/verify Confirm the 6-digit code. On success returns an smsPinToken (a 30-minute verified marker is set). Body: { phone, code }. Response: { "ok": true, "data": { "smsPinToken": "", "smsPinPhone": "" } } Pass smsPinToken + smsPinPhone into /submit; the token also exempts the submit from captcha. ## Hep.gg dashboard (cookie auth - building forms + reading submissions) The form builder and the submission inbox are dashboard-only and not part of the public API: list/create/edit/delete forms under /api/v1/forms, and read submissions under /api/v1/forms/:id/submissions.