Form reception for agents — your form stays on your own site, the definition and the answers live on your own server, and every answer fans out to a signed webhook and a push notification.
pks-agent-quickform is form reception for agents. It owns forms — it does
not own form pages.
Your marketing site, your customer portal, your landing page: those stay exactly
where they are, on your domain, in your code. The server hands out the form
definition, receives the answers, stores them as files under your own
USER_DATA_DIR, and fans each one out to a signed webhook and a Web Push
notification.
It ships as one Go binary in one container, in the same shape as its siblings
(pks-agent-inbox, pks-agent-consent, pks-agent-share): stdlib net/http,
folder-state storage, no database.
submitUrlThe whole integration is one line — and it is a line we write, not you.
A QuickForm definition can carry a
submit block:
{ "submit": { "submitUrl": "…", "submitMethod": "POST" } }
GET /api/orgs/{org}/forms/{form}/definition injects that block into whatever
definition you stored, pointing at this server's ingest endpoint for that exact
form. So the page that renders your form:
@eavfw/quickform-core,Change the form, change the org, move the server: the page keeps working, because the address it submits to arrived with the questions.
Do not pass
onSubmitAsync. The core runtime only honourssubmitUrlwhen no submit handler is supplied — a handler bypasses the seam entirely and the answers never reach the server.
orgs/<org>/forms/<form>/submissions/ with its timestamp, origin, referer,
IP and the untouched raw body.X-QuickForm-Signature: sha256=<hex(hmac)> over the exact
bytes we posted, so the receiving agent can prove the call came from you.| Surface | Endpoints | Auth |
|---|---|---|
| Public (the customer's page) | GET …/definition, POST …/submissions | none — gated by the form's origin allow-list |
| Admin (PWA, portal, agents) | orgs, forms, definitions, submissions, push | Authorization: Bearer <token> |
The public pair is unauthenticated by design — anyone who can load the form can post to it, exactly as with any form on the open web. The defences are the per-form origin allow-list and a 256 KiB body cap, not a secret the browser would have to carry.
There is no published image to pull: the hosted instance is git-built by Coolify straight from the repository, so you build the same Dockerfile yourself.
git clone https://github.com/pksorensen/pks-agent-quickform
docker build -t quickform pks-agent-quickform/src/quickform-server
docker run --rm -p 8080:8080 \
-e QUICKFORM_ADMIN_TOKEN=$(openssl rand -hex 32) \
-e PUBLIC_BASE_URL=https://forms.example.com \
-e USER_DATA_DIR=/data -v $PWD/quickform-data:/data \
quickform
Or, straight from a checkout, with no container at all:
cd src/quickform-server
PORT=8123 USER_DATA_DIR=./user-data PUBLIC_BASE_URL=http://localhost:8123 go run .
With no QUICKFORM_ADMIN_TOKEN the server runs open — the single-user
dev mode its siblings default to. That is correct on your laptop and wrong on
the internet: an open instance lets anyone read every org's answers and mint
tokens. Set the token before the first public deploy, not after.
| Variable | Default | Meaning |
|---|---|---|
PORT | 8080 | HTTP listen port |
USER_DATA_DIR | ./user-data | Root of the folder-state store |
QUICKFORM_ADMIN_TOKEN | (empty) | Global admin credential. Empty ⇒ open/dev mode |
PUBLIC_BASE_URL | (derived per request) | Externally reachable origin used to build submitUrl and the embed URL. Set it whenever anything reaches the server through a proxy or from another container |
WEB_DIR | ./web | Static PWA root |
PUSH_CONTACT | quickform@agentics.dk | VAPID contact — bare, no mailto: prefix |
VAPID keys are generated into USER_DATA_DIR on first boot; keep that volume and
push subscriptions survive a restart.
There is deliberately no form designer in this server. QuickForm's own designer was built for humans clicking a canvas; the plan is a chat-driven, AI-first designer in the portal that exposes QuickForm, where an agent writes the definition and shows you the result. Until then, the definition is JSON — a format both a person and an agent can write.
Every quickform-server endpoint — the two public ones your form page uses, and the admin surface the PWA, the portal and your agents use.
From an empty server to a received answer — create an org, store a definition, render it on your own page, and watch the submission land.
The payload quickform-server posts on every submission, the X-QuickForm-Signature HMAC, and how an agent verifies it.