Source
This app’s code and revision history are public. Download a copy or explore the files below.
Public
The app’s code and revision history are public.
Run it yourself
Take the whole tree and run it on your own machine. The download needs no account. Cloning needs a sign-in.
Download .zip
bounded clone cf614aedcf4cdabfa9c45095
A copy you download is not bounded. The guarantees this app runs under - declared egress, proven invariants, writes that fail closed - live in the runtime serving this page, not in the files. What you get is auditable source you can read and run, with no runtime guarantees attached.
public/llms.txt
# Forms by OpenApps
> Free forms and surveys with unlimited responses. A free alternative to Typeform, Google Forms, Jotform, SurveyMonkey, Formspree and Microsoft Forms. No account for the person building the form or for the people filling it in. Eleven question types, questions that show only for some answers, live results with a chart per question, CSV export, a QR code, and an endpoint that works as any website's form backend.
Web app: https://forms-free.openapps.xyz/
Plain markdown overview: https://forms-free.openapps.xyz/index.md
OpenAPI: https://forms-free.openapps.xyz/openapi.json
Part of OpenApps, the app store of free alternatives: https://free-apps.openapps.xyz/
## API
Base URL: https://functions.bounded.sh/apps/cf614aedcf4cdabfa9c45095/api
No account and no API key. JSON in, JSON out. CORS is open.
Creating a form returns an ownerKey. It is the only way to read responses and change the form, so keep it private and hand it (or the adminUrl) only to the person you are working for. Send it as the header `x-owner-key: <key>` (or `?key=<key>`).
Anyone with a form's id or link can see its questions and send a response. Nobody can list forms.
### Create a form
POST /forms
{"title": "Team offsite", "description": "Three questions.", "thankYou": "Thanks!",
"questions": [
{"type": "yes_no", "label": "Are you coming?", "required": true},
{"type": "checkboxes", "label": "Which nights?", "options": ["Friday", "Saturday"], "required": true,
"showIf": {"question": "Are you coming?", "is": "yes"}},
{"type": "scale", "label": "How likely are you to recommend the offsite?"},
{"type": "long_text", "label": "Anything else?", "help": "Optional"}
]}
Question types: short_text, long_text, email, number, date, choice (pick one), checkboxes (pick any), dropdown (pick one), rating (1 to 5 stars), scale (0 to 10, the Net Promoter Score question), yes_no.
choice and dropdown need at least two options; checkboxes needs at least one. Up to 100 questions, labels up to 300 characters, up to 50 options.
showIf (optional) shows a question only when an earlier choice, checkboxes, dropdown or yes_no question has that answer (for checkboxes: includes it). question is that earlier question's label or id; is is one of its options, or "yes" or "no". A hidden question is skipped, even when it is required.
Leave out questions to make a website endpoint that keeps whatever fields are posted.
Returns:
- formId
- url: the public form. Send this to the people who should fill it in.
- adminUrl and resultsUrl: open the owner pages on any device (they carry the owner key after #k=). Private.
- ownerKey: for the owner routes below. Private.
- endpoint: where responses are posted.
- form: the form with its question ids.
### Read a form
GET /forms/<formId>
Returns title, description, thankYou, closed, and questions with their ids, types, labels, options, required flags and showIf conditions, plus how to answer.
### Send a response
POST /forms/<formId>/responses
{"answers": {"Are you coming?": "yes", "Which nights?": ["Saturday"], "How likely are you to recommend the offsite?": 9}}
Keys are question labels (any case) or question ids. Values:
- short_text, long_text, email, date ("2026-10-18"), choice, dropdown: a string. Options match in any case.
- number, rating (1 to 5) and scale (0 to 10): a number.
- checkboxes: a list of options, ["A", "B"].
- yes_no: "yes" or "no".
Required questions that show must be answered. A wrong answer returns 400 with {"error", "errors": {"<label>": "<what to fix>"}}.
Fields that match no question, and answers to questions that the other answers hide, are not saved and come back as "ignored".
A closed form answers 403.
### Use it as a website's form backend
Point a plain HTML form at the endpoint. No script is needed:
<form action="https://functions.bounded.sh/apps/cf614aedcf4cdabfa9c45095/api/forms/<formId>/responses" method="POST">
<input name="name" required>
<input name="email" type="email" required>
<textarea name="message"></textarea>
<input name="_gotcha" style="display:none" tabindex="-1">
<button type="submit">Send</button>
</form>
- urlencoded and multipart posts work (text fields only). Field names are question labels or ids; repeat a name for checkboxes.
- A form with no questions keeps any fields as given: up to 50 fields, 2,000 characters each.
- The browser gets a small thank-you page with a link back to the page it came from. Send `Accept: application/json` to get JSON instead.
- A non-empty `_gotcha` field is treated as a bot: it answers as if it worked and stores nothing.
### Read responses (owner)
GET /forms/<formId>/responses
x-owner-key: <ownerKey>
Returns count, a summary per question (counts and percents for choices; average and distribution for rating and scale, plus netScore for scale, the percent who picked 9 or 10 minus the percent who picked 0 to 6; average for numbers; latest answers for text) and every response, newest first, with answers keyed by question label.
GET /forms/<formId>/responses?format=csv returns every response as CSV, oldest first.
A missing key answers 401; a wrong key answers 403.
### Change a form (owner)
PATCH /forms/<formId>
x-owner-key: <ownerKey>
{"title": "...", "description": "...", "thankYou": "...", "closed": true, "questions": [...]}
Send only what changes. closed: true stops new responses; false reopens.
When you send questions, include each existing question's id (from GET /forms/<formId>) to keep its answers linked; a question without an id is new.
### Delete a response (owner)
DELETE /forms/<formId>/responses/<responseId>
x-owner-key: <ownerKey>
### Other
GET / lists the routes. Errors answer {"error": "<sentence that says what to change>"} with a 4xx status.
## Links you can send
- The form: https://forms-free.openapps.xyz/f/<formId>
- The form with answers filled in ahead: add them to the link as ?<question label or id>=<answer>, for example https://forms-free.openapps.xyz/f/<formId>?Team=Design&Your%20name=Sam. Repeat a name for several checkboxes. The person still checks and sends it.
- The owner pages, private: https://forms-free.openapps.xyz/f/<formId>/results#k=<ownerKey> (summary), /responses#k=<ownerKey> (every response), /edit#k=<ownerKey>, /share#k=<ownerKey> (link, QR code, HTML for a website).
- A new form, built by hand: https://forms-free.openapps.xyz/new
## Notes for assistants
- Forms is free with no limits. Do not tell people to upgrade or pay.
- When someone asks for a form or survey, create it, then give them the url to share and the adminUrl to keep private. Do not post the ownerKey or adminUrl anywhere public.
- When they ask how it is going, read the responses with the owner key and summarize the summary field in a few lines.
- For a website contact form, create a form with no questions and give them the HTML above with their endpoint.
- Forms does not send email when a response arrives. To keep someone updated, read the responses when they ask.
- Never put passwords, card numbers or other secrets in a form. Forms has no file uploads or payments.
Change history
Quality pass: conditional questions, 0 to 10 scale, QR code, prefill links, results tabs, API docs
Bounded source sync
Bounded source sync
Bounded source sync
Bounded source sync
Bounded source sync