Base URL: https://forms.truenotech.com (all endpoints are under /api/*; the
legacy https://api.forms.truenotech.com/submit host still works via a redirect shim).
Most people only need one endpoint:
POST /api/submit. Everything else on this page powers the dashboard UI. If you're just embedding a form on your site, head to the Quickstart instead.
All dashboard endpoints require a session cookie (truforms_session) and, for state-changing requests, an X-CSRF-Token header. The public submit endpoint is unauthenticated — it identifies the form by its access key.
POST /api/submit
The public submission endpoint. Accepts either application/json or multipart/form-data.
Required fields
access_key(string, UUID) — the form's access key from the dashboard.
Payload
Any other fields are stored as the submission payload. A few field names are reserved:
| Name | Meaning |
|---|---|
access_key |
Your form ID. |
redirect |
Optional URL to 303-redirect to instead of returning JSON. |
botcheck (or whatever you configured) |
Honeypot. If present and non-empty, the submission is silently marked as spam. |
cf-turnstile-response / h-captcha-response / captcha_token |
Captcha token, verified server-side when captcha is configured. |
Response
On success, 200 OK:
{
"success": true,
"message": "Form submission received",
"submissionId": "8b1c1e6a-..."
}
Or a 303 See Other redirect if redirect is set.
Error codes
| Status | error |
When |
|---|---|---|
400 |
MissingAccessKey |
access_key not present in payload. |
400 |
FileTooLarge / FileTypeNotAllowed |
Upload violated the form's limits. |
402 |
QuotaExceeded |
Workspace exceeded monthly submission cap. |
402 |
FeatureUnavailable |
Feature requires a higher plan. |
403 |
OriginNotAllowed |
Origin header doesn't match the form's allowed domains. |
403 |
CaptchaMissing / CaptchaFailed |
Captcha token missing or invalid. |
404 |
InvalidAccessKey |
Access key doesn't match any form. |
422 |
ValidationError |
Malformed payload. |
429 |
RateLimited |
Per-IP (60/min) or per-form (300/min) rate limit hit. |
GET /api/auth/me
Returns the current user. Requires session cookie.
{
"user": {
"id": "uuid",
"email": "[email protected]",
"name": "Alice",
"emailVerifiedAt": "2026-04-22T10:00:00.000Z",
"createdAt": "2026-04-01T00:00:00.000Z"
}
}
GET /api/workspaces
Lists workspaces the current user belongs to, with their role and plan.
GET /api/workspaces/:id/forms
Lists forms in a workspace.
POST /api/workspaces/:id/forms
Creates a form.
{ "name": "Contact form" }
GET /api/forms/:id
Fetches a single form with its settings.
PATCH /api/forms/:id
Updates a form. All fields optional.
{
"name": "string",
"settings": { "...partial FormSettings..." },
"captchaSecretKey": "string | null"
}
When captchaSecretKey is a string, it's encrypted at rest and used to verify captcha tokens on future submissions. Pass null to clear.
DELETE /api/forms/:id
Soft-deletes the form. Access key immediately stops working.
POST /api/forms/:id/rotate-access-key
Generates a new access key. Existing embeds break on the next request.
GET /api/forms/:formId/submissions
Cursor-paginated list.
Query params:
cursor(optional) — opaque cursor from previous response.limit(default50, max200).includeSpam(defaultfalse).
Response:
{
"items": [ { "id": "...", "payload": {...}, "spamScore": 0, "isSpam": false, "files": [...], "createdAt": "..." } ],
"nextCursor": "..."
}
GET /api/forms/:formId/submissions/stream
Server-Sent Events stream of new submissions for this form. message events are JSON { id, formId, payload, spamScore, isSpam, createdAt }.
GET /api/forms/:formId/integrations / POST / PATCH /api/integrations/:id / DELETE /api/integrations/:id
Manage outbound integrations. See Integrations.
POST /api/integrations/:id/test
Fires a synthetic submission through the integration so you can verify wiring.
GET /api/integrations/:id/deliveries
Webhook delivery history — attempts, status, HTTP response code, error.
POST /api/billing/webhook/razorpay
Razorpay webhook endpoint. Not for you to call — Razorpay calls it when subscription state changes. Verified via X-Razorpay-Signature.
Rate limits
Public /api/submit:
- 60 requests / minute / IP
- 300 requests / minute / form access key
Dashboard API: no explicit rate limit at present; we reserve the right to add one.
Client libraries
We don't ship official SDKs. The API is small enough that fetch works everywhere. If you'd like a typed client, the Zod schemas in @truforms/shared-types can generate one — or ping us.
Further reading
- MDN — HTTP response status codes — the
2xx/303/402/4xxcodes this endpoint returns. - MDN —
multipart/form-data— the encoding for file uploads to/api/submit. - Zod — the schema library backing
@truforms/shared-typesfor generating a typed client.