Help center
Go to home
Go to templates
Go to settings
Go to help center

The REST API

Create and update forms, list and delete submissions and manage webhooks over a bearer-key REST API served by your own install.

The REST API gives a script the same reach over your forms that you have in the app. Create a form for each new client, keep a dropdown's options in step with a live source, pull submissions into a warehouse, wire a webhook from a deploy script. Every install serves it under its own address, at /api/v1, and the reference for each endpoint lives on that install at /developers.

The round cat holding up a key beside a small padlock

Developer docs

The endpoint reference is served by Tinyform itself at /developers, and it is generated from the same code that answers the requests, so it cannot describe a route that is not there. The rail in the app, the API keys page and the help bubble all link to it.

API basics

  • Base URL: your own Tinyform address plus /api/v1, for example https://forms.example.com/api/v1. There is no separate API host.
  • Authentication: create a key under Settings, then API keys, with Create API key. Give it a name; the key, which starts with fk-, is shown once with a copy button. Only a hash of it is stored, so a lost key is deleted and replaced, never recovered. Send it on every request as Authorization: Bearer fk-....
  • Who a key is: the person who created it. It sees their workspaces and their forms, through the same membership checks the app uses.
  • Rate limit: 100 requests per minute per key, and 300 per minute per client address. Past either, the answer is 429 with a Retry-After header. Use a webhook rather than polling for new submissions.
  • Versioning: there is no version header. /developers always describes what the API serves today, so it is the reference to read rather than a version number to pick.
  • Paging: list endpoints return an envelope with items, page, limit and hasMore. Pass ?page (starting at 1) and ?limit (default 50, at most 500) and keep going while hasMore is true.
  • Errors: a JSON body of the shape error: "..." with the matching status. Every path under /api/v1 that does not exist, in any method, answers a JSON 404, never the app's HTML not-found page.
CodeMeaning
200The request succeeded
401The key is missing, malformed or deleted
403The change is refused for this workspace, for instance branding: false without the entitlement
404Not found, or not yours: the two are deliberately the same answer, so the API never confirms that a form exists
429Rate limited; wait for the Retry-After seconds
500Something failed at the other end; the response says no more than that

Forms in the Trash are invisible on this surface. A deleted form is a 404 until it is restored in the app.

Endpoints

Method and pathWhat it does
GET /api/v1/users/meThe person the key acts as
GET /api/v1/workspacesThe workspaces they belong to
GET /api/v1/workspaces/:idOne workspace
GET /api/v1/formsTheir forms, newest first; ?workspaceId= narrows it
POST /api/v1/formsCreate a draft from workspaceId, an optional name and an optional document
GET /api/v1/forms/:idOne form, with a status of draft, published or closed
PATCH /api/v1/forms/:idUpdate name, document or settings; publishing and closing happen in the app
DELETE /api/v1/forms/:idMove the form to the Trash
GET /api/v1/forms/:id/questionsThe columns the Submissions table draws, with each one's label, type and key
GET /api/v1/forms/:id/submissionsResponses, ?filter=all, completed or partial
GET /api/v1/forms/:id/submissions/:sidOne response
DELETE /api/v1/forms/:id/submissions/:sidDelete one response
GET /api/v1/webhooksOne form's webhooks; ?formId= is required
POST /api/v1/webhooksAdd a webhook to a form
PATCH /api/v1/webhooks/:idChange its URL, secret, headers or enabled state
DELETE /api/v1/webhooks/:idRemove it
GET /api/v1/webhooks/:id/deliveriesDelivery receipts: status, attempts, lastError, submissionId, eventType and dates
POST /api/v1/webhooks/:id/deliveries/:did/retrySend one delivery again

questions and a submission's answers share one vocabulary: label is the column's name as the table heads it (the field name where you renamed one, otherwise the question), key is where the answer sits in the submission, and the list includes a named hidden field, the respondent's country and each calculated field. A FILE or SIGNATURE answer also carries files, a list of name and url pairs where url is a signed link to the attachment, valid for seven days.

What people build with it

Create and personalise forms

Fetch a form you use as a template with GET /api/v1/forms/:id, change what needs changing in its document, and POST /api/v1/forms with the workspace id and the new document to create a draft for a new client. Publish it from the editor, or leave it as a draft for someone to finish.

curl -X POST https://forms.example.com/api/v1/forms \
  -H "Authorization: Bearer fk-..." \
  -H "Content-Type: application/json" \
  -d '{ "workspaceId": "ws_...", "name": "Client intake - Acme Co", "document": { ...copied from GET /api/v1/forms/:id... } }'

Keep a form in step with live data

Read the form, change the option list of a dropdown in its document, and send the whole document back with PATCH /api/v1/forms/:id. The document is replaced as a unit, so always start from a fresh GET rather than a copy you kept earlier, or a block edited in the app since then is lost.

curl https://forms.example.com/api/v1/forms/aB3dE4fG \
  -H "Authorization: Bearer fk-..."

curl -X PATCH https://forms.example.com/api/v1/forms/aB3dE4fG \
  -H "Authorization: Bearer fk-..." \
  -H "Content-Type: application/json" \
  -d '{ "document": { ...the full document, with the options updated... } }'

PATCH also takes settings, and it applies the same rules the Settings tab does: asking for branding: false in a workspace without the entitlement is refused with 403.

Read and manage submissions

List a form's responses and forward them downstream, filtering to completed ones only:

curl "https://forms.example.com/api/v1/forms/aB3dE4fG/submissions?filter=completed&limit=100" \
  -H "Authorization: Bearer fk-..."

Or skip polling: add a webhook and each completed submission is pushed to your endpoint the moment it is stored, without counting against the rate limit.

Delete a response you no longer need:

curl -X DELETE https://forms.example.com/api/v1/forms/aB3dE4fG/submissions/k3Jd8sPq2 \
  -H "Authorization: Bearer fk-..."

The answer is 204. The row and every file attached to it are removed together, and the form's response count goes down by one, the same outcome as the Submissions table's bin.

Frequently asked questions

How do I create an API key?

Open Settings, then API keys, and click Create API key. Name it, copy the key from the confirmation, and keep it somewhere safe: it is not shown again. Deleting a key on that page revokes it immediately.

Is the API free?

Yes. It is part of every install, and nothing on it is gated by plan. The one plan-aware rule is the branding setting noted above, which the API enforces exactly as the app does.

What can I build with it?

Anything that creates or edits forms, reads or deletes submissions, or manages webhooks: a client portal that provisions a form per customer, an onboarding flow, a sync into your own database, an internal dashboard. What it does not do is publish or close a form; those stay in the app.

Can I use it with an automation tool?

Yes, as long as the tool can make HTTP requests with a bearer header. For reacting to new submissions, though, a webhook is the better trigger: it needs no key and no polling.

How do I authenticate?

With a bearer token: Authorization: Bearer fk-.... A request without a valid key is answered 401.

Do I get webhooks or an API?

Both, and they are managed from the same place. Webhooks push a submission to you as it arrives; the API lets you read and change everything at your own pace, including the webhooks themselves.

Can I prefill a form's answers over the API?

Prefilling is done on the published link rather than in the API: see Pre-populate form fields and Hidden fields. What the API can do is create or update the form the link points at.

Is there a rate limit?

Yes: 100 requests per minute per key and 300 per minute per client address, answered with 429 and a Retry-After header when exceeded. Use a webhook for anything that would otherwise poll.

How do I keep an integration stable?

Read /developers again after an upgrade: it always describes what the API serves today. There is no per-request version header to select an older behaviour.