Agent skill
Form.io Webhooks Skill
Receive and authenticate Form.io Webhook actions. Use when setting up a Form.io webhook endpoint, verifying the HTTP Basic credentials (Authorize User / Authorize Password), debugging 401s, or handling submission create (POST), update (PUT) and delete (DELETE) requests.
Install this skill
npx skills add hookdeck/webhook-skills --skill formio-webhooks
The Form.io Webhook action calls an external URL when a form submission is created, updated or deleted. Requests are not signed. If you fill in Authorize User and Authorize Password on the action, Form.io sends them as Authorization: Basic base64(user:pass), and your endpoint authenticates the request by checking that header.
A static shared credential authenticates the sender, not the body, and gives no replay protection (no signature, no timestamp). Use HTTPS, and rotate the credential if it leaks.
When to Use This Skill
- How do I receive Form.io webhooks?
- How do I authenticate a Form.io Webhook action (Authorize User / Authorize Password)?
- Does Form.io sign webhook payloads? (No: use HTTP Basic auth)
- How do I tell a submission create from an update or delete? (By HTTP method)
- Why does my Form.io webhook endpoint return 401 or 405?
Verification (core)
const crypto = require('crypto');
const sha = (b) => crypto.createHash('sha256').update(b).digest();
const safeEqual = (a, b) => crypto.timingSafeEqual(sha(a), sha(b));
function verifyFormioRequest(headers, user = process.env.FORMIO_WEBHOOK_USERNAME,
pass = process.env.FORMIO_WEBHOOK_PASSWORD) {
if (!user || !pass) return false; // never accept when unconfigured
const m = /^basic +([A-Za-z0-9+/]+={0,2}) *$/i.exec(headers.authorization || '');
if (!m) return false;
const decoded = Buffer.from(m[1], 'base64');
const i = decoded.indexOf(0x3a); // split on the FIRST colon
if (i < 0) return false;
const userOk = safeEqual(decoded.subarray(0, i), Buffer.from(user));
const passOk = safeEqual(decoded.subarray(i + 1), Buffer.from(pass));
return userOk && passOk; // both evaluated, no short-circuit
}
- Header:
Authorization: Basic base64(username:password). Scheme is case-insensitive; split on the first colon because passwords may contain colons. - Compare username and password each in constant time, and evaluate both before deciding. In Python use
hmac.compare_digeston bytes (encode both sides;strraises on non-ASCII). - Return
401for a missing, malformed or mismatched header,400for a non-empty body that isn't a JSON object,200once handled. - Read the body raw (
express.raw({ type: '*/*' })) and authenticate before parsing it. - Accept more than POST. The request method is set in the action. With the default Match method Form.io sends POST for creates, PUT for updates and DELETE for deletes (URL params go in the query string). The HTTP verb is the only hint of the operation: there is no event field. The examples accept POST, PUT, PATCH, GET and DELETE and handle an empty body.
- Hookdeck: the Form.io source pre-selects POST only. Allow PUT and DELETE on the source (and any other methods your action uses), or those requests are rejected.
For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.
Operations
| Method | Operation (Match) | Body |
|---|---|---|
POST | Submission create | { request, response, submission, params } |
PUT / PATCH | Submission update | { request, response, submission, params } |
DELETE | Submission delete | May be empty; params arrive as a query string |
GET | Only if chosen in the action | Empty |
Environment Variables
FORMIO_WEBHOOK_USERNAME=your-webhook-username # "Authorize User" on the Webhook action
FORMIO_WEBHOOK_PASSWORD=your-webhook-password # "Authorize Password" on the Webhook action
Local Development
Form.io must reach your endpoint over the network. Tunnel with the Hookdeck CLI (no account required):
npx hookdeck-cli listen 3000 formio --path /webhooks/formio
Use the printed URL as the Request URL of the Webhook action.
Resources
- references/overview.md - What the Webhook action sends, methods and payload shape
- references/setup.md - Add the Webhook action and set the credentials
- references/verification.md - Basic auth check details, gotchas, debugging 401s
- examples/ - Runnable Express, Next.js and FastAPI handlers with tests