Guides · REST API
Send a request with the REST API
Any tool with an HTTP step can send a request: Pipedream, a serverless function, a script. This guide does it with curl, so you can see each call before you move it into your own code.
Last checked
You need a published form. This guide uses the one built in Build a form with an AI agent: company name, contact email, VAT number, a yes or no question about payment terms, and a file upload. Every method and option used here is described in API methods.
1. Create an API token
In formbase, open OAuth and API Keys in the workspace sidebar and create a token. The value is shown once. Keep it in an environment variable, not in your code:
export FORMBASE_TOKEN='fb_...'A token reaches the one workspace it was created in. Every call below is a POST to the same URL with the token in the
Authorization header; the body names the method and its parameters.
2. Find the form ID
Open the form in the editor. The form ID is the part of the address after /forms/:
https://app.formbase.so/<workspace ID>/forms/jx75hdx8vb5hy1x85gm17nqgkn8f674g/editThe commands below use this guide’s form ID. Put your own in its place.
3. List the field keys
A request fills in questions, and the answers come back, by field key. Ask the form for its keys instead of guessing them from the titles:
curl -X POST https://api.formbase.so/api/v1 \
-H "Authorization: Bearer $FORMBASE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"method": "fields.list", "params": {"formId": "jx75hdx8vb5hy1x85gm17nqgkn8f674g"}}'The reply lists each question of the published form. Shortened:
{
"ok": true,
"data": {
"published": true,
"items": [
{ "key": "company_name", "type": "text", "title": "Company name", "required": true, "prefillable": true },
{ "key": "contact_email", "type": "email", "title": "Contact email", "required": true, "prefillable": true },
{ "key": "vat_number", "type": "text", "title": "VAT number", "required": false, "prefillable": true },
{
"key": "do_you_accept_our_30_day_payment_terms", "type": "radio", "title": "Do you accept our 30-day payment terms?", "required": true, "prefillable": true,
"options": [{ "key": "yes", "label": "Yes" }, { "key": "no", "label": "No" }]
},
{ "key": "certificate_of_incorporation", "type": "file", "title": "Certificate of incorporation", "required": true, "prefillable": false }
],
"hasMore": false
}
}To prefill a choice question, send the option’s key, here “yes”, not its label. A question with
prefillable: false, like the file upload, only the recipient can answer. The other flags are explained under
fields.list.
4. Create a test request
Send the request to Ada Lovelace, with the company name filled in and locked. test: true makes it a
test request: it never emails anyone and counts nowhere, so you can repeat this step
as often as you like, with a new idempotencyKey each time.
curl -X POST https://api.formbase.so/api/v1 \
-H "Authorization: Bearer $FORMBASE_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"method": "requests.create",
"params": {
"formId": "jx75hdx8vb5hy1x85gm17nqgkn8f674g",
"recipient": { "email": "ada@acme.example", "name": "Ada Lovelace" },
"prefill": { "company_name": "Analytical Engines Ltd" },
"readonly": ["company_name"],
"externalId": "supplier-2043",
"idempotencyKey": "supplier-2043",
"test": true
}
}'prefill fills in answers the recipient can still change. A key in readonly is locked as well.
externalId is your own id for this piece of work, like a supplier number. It comes back on every read and callback.
idempotencyKey makes a retry safe. If your code sends the same call twice, formbase returns the first request with
deduplicated: trueinstead of creating a second one.
The reply carries the request ID and the link for the recipient:
{
"ok": true,
"data": {
"id": "m17ayhcnj9xvzkff3atek49bdd8f6872",
"status": "pending",
"url": "https://form.formbase.so/r/rq_...",
"deliveryStatus": "not_requested",
"externalId": "supplier-2043",
"deduplicated": false,
"createdAt": 1790539225370,
"expiresAt": 1793131225370
}
}deliveryStatus: “not_requested” means formbase sent no email; you deliver the link yourself. To have formbase send the
invitation and reminders, add “delivery”: “email”, which needs a Pro or Business plan. See
Invitations, reminders & expiry. If the form has Send reminders on, a
real request with a recipient email still gets the scheduled reminders; add “reminders”: [] to send none.
Test requests stay out of sight
The Requests page lists test requests only when you turn on the Show test requests
filter. Open the url yourself to complete it. Drop test when you send the real thing.
5. Complete it and read the answers
Open the url in a browser. The company name is filled in and locked; answer the rest and submit. Then read the request back
with its ID:
curl -X POST https://api.formbase.so/api/v1 \
-H "Authorization: Bearer $FORMBASE_TOKEN" \
-H "Content-Type: application/json" \
-d '{"method": "requests.get", "params": {"requestId": "m17ayhcnj9xvzkff3atek49bdd8f6872"}}'Once status is completed, the reply holds two maps keyed by field key. answers is for code: a
choice answer is the option key, a file answer is a list of files with download URLs. display is for people: labels and file
names.
"answers": {
"company_name": "Analytical Engines Ltd",
"do_you_accept_our_30_day_payment_terms": "yes",
"certificate_of_incorporation": [{ "name": "certificate-of-incorporation.pdf", "type": "application/pdf", "size": 635, "url": "https://api.formbase.so/api/storage/..." }]
},
"display": {
"company_name": "Analytical Engines Ltd",
"do_you_accept_our_30_day_payment_terms": "Yes",
"certificate_of_incorporation": "certificate-of-incorporation.pdf"
}Asking again and again until the status changes works for a test, but not in production. Give the request a callbackUrl and
formbase calls you when it ends; the next guide sets that up.