Developers
The form backend you don’t have to build
Validation, conditional logic, file handling, scoring, storage and delivery — behind one clean REST API.
Scoped API keys
forms:read, forms:write, responses:read, responses:write. Keys are shown once and stored as keyed hashes.
Forms as JSON
Version-controlled definitions: sections, items, conditions and approval rules in one readable document.
Signed webhooks
response.created, response.approval_changed and response.deleted — HMAC-SHA256 signed, retried with backoff.
Headless forms
Fetch a public form schema and render it in your own UI; submit to the same validation engine.
Predictable limits
Per-key rate limits with Retry-After headers and clear 402 errors when a plan limit is reached.
Safe by default
Strict input validation, encrypted storage and no browser access for API keys (CORS-restricted).
1. Create
Create a form from a template or JSON
curl -X POST https://create.abaworks.dev/api/v1/forms \
-H "Authorization: Bearer $CAF_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "template": "contact-form", "title": "Website contact" }'2. Read
List approved responses
import requests
API = "https://create.abaworks.dev/api/v1"
headers = {"Authorization": f"Bearer {API_KEY}"}
page = requests.get(f"{API}/forms/{FORM_ID}/responses", headers=headers,
params={"approval": "approved", "limit": 100}).json()
for r in page["data"]:
print(r["id"], r["answers"]["email"], r["approval"]["percent"])3. React
Verify webhook signatures in a few lines
import crypto from "node:crypto";
// Verify a webhook from Create a Form (Express example)
app.post("/webhooks/caf", express.raw({ type: "application/json" }), (req, res) => {
const header = req.get("x-createaform-signature") ?? "";
const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
const expected = crypto
.createHmac("sha256", process.env.CAF_WEBHOOK_SECRET)
.update(`${t}.${req.body}`)
.digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(t)) < 300;
if (!fresh || !crypto.timingSafeEqual(Buffer.from(v1, "hex"), Buffer.from(expected, "hex"))) {
return res.sendStatus(400);
}
const event = JSON.parse(req.body);
console.log(event.type, event.data.response.approval?.status);
res.sendStatus(200);
});Ship your next form in an afternoon.
Free plan includes API access. No credit card required.