formbasedocs
Go to appApp

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:

bash
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/:

text
https://app.formbase.so/<workspace ID>/forms/jx75hdx8vb5hy1x85gm17nqgkn8f674g/edit

The 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:

bash
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:

json
{
  "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.

bash
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: true instead of creating a second one.

The reply carries the request ID and the link for the recipient:

json
{
  "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:

bash
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.

json
"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.

Next