Deployment Guide
Step-by-step instructions to deploy your own instance of formdata.dev on Cloudflare.
Step 1 — Clone the Repository
git clone https://github.com/nicoswan/formdata.dev.git
cd formdata.dev
Step 2 — Install Dependencies
Step 3 — Authenticate with Cloudflare
This opens a browser window to authorize Wrangler with your Cloudflare account.
Step 4 — Create the D1 Database
wrangler d1 create formdata-db
Copy the database_id from the output. You will need it for Step 7.
Step 5 — Create the KV Namespace
wrangler kv namespace create FORM_KV
Copy the id from the output.
Step 6 — Create the Queues
wrangler queues create formdata-delivery
wrangler queues create formdata-delivery-dlq
Step 7 — Update wrangler.jsonc
Open wrangler.jsonc and replace the resource IDs with the ones from the previous steps:
{
"name": "formdata-worker",
"main": "src/index.ts",
"compatibility_date": "2026-02-24",
"compatibility_flags": ["nodejs_compat"],
"d1_databases": [
{
"binding": "DB",
"database_name": "formdata-db",
"database_id": "YOUR_D1_DATABASE_ID",
"migrations_dir": "migrations"
}
],
"kv_namespaces": [
{
"binding": "FORM_KV",
"id": "YOUR_KV_NAMESPACE_ID"
}
],
"queues": {
"producers": [
{
"binding": "DELIVERY_QUEUE",
"queue": "formdata-delivery"
}
],
"consumers": [
{
"queue": "formdata-delivery",
"max_batch_size": 10,
"max_retries": 5,
"dead_letter_queue": "formdata-delivery-dlq",
"retry_delay": 30
}
]
},
"durable_objects": {
"bindings": [
{
"name": "MCP_AGENT",
"class_name": "FormDataMcpAgent"
}
]
},
"migrations": [
{
"tag": "v1",
"new_sqlite_classes": ["FormDataMcpAgent"]
}
]
}
Step 8 — Run D1 Migrations
wrangler d1 migrations apply formdata-db --remote
This creates the organizations, organization_api_keys, forms, and destinations tables along with their indexes.
Step 9 — Set Secrets
If you want subdomain provisioning (custom hostnames), set these secrets:
wrangler secret put CF_ZONE_ID
# Paste your Cloudflare Zone ID when prompted
wrangler secret put CF_API_TOKEN
# Paste a Cloudflare API token with Zone > Custom Hostnames > Edit permission
Optional Secrets
CF_ZONE_ID and CF_API_TOKEN are only required for the subdomain provisioning feature. If you skip them, account creation still works — it just will not provision <slug>.yourdomain.com hostnames automatically.
Step 10 — Type Check
Verify there are no TypeScript errors before deploying.
Step 11 — Deploy
Wrangler will output your Worker's URL:
https://formdata-worker.your-account.workers.dev
Step 12 — Add a Custom Domain (Optional)
- Go to Cloudflare Dashboard > Workers & Pages > formdata-worker > Settings > Domains & Routes
- Click Add Custom Domain
- Enter your domain (e.g.,
api.yourdomain.com)
- If there is an existing A or CNAME record for that subdomain, delete it first
- Cloudflare will create the required DNS record automatically
Step 13 — Verify
Create a test account to confirm everything is working:
curl -X POST https://YOUR_WORKER_URL/v1/admin/accounts \
-H "Content-Type: application/json" \
-d '{
"organizationName": "Test Org",
"ownerEmail": "[email protected]",
"slug": "test"
}'
You should receive a response with ok: true, an account object, and a tenantKey.
Then create a form and test the ingestion endpoint:
# Create a form (replace TENANT_KEY with the tenantKey from above)
curl -X POST https://YOUR_WORKER_URL/v1/admin/forms \
-H "Content-Type: application/json" \
-H "x-tenant-key: TENANT_KEY" \
-d '{
"name": "Test Form",
"slug": "test-form"
}'
# Submit to the form (replace PUBLIC_KEY with the publicKey from above)
curl -X POST https://YOUR_WORKER_URL/v1/f/PUBLIC_KEY \
-H "Content-Type: application/json" \
-d '{"message": "Hello from self-hosted formdata.dev!"}'
Expected Response
The submission should return 202 with ok: true. If no destinations are configured, queuedDestinations will be 0 — that is expected.
Updating
To deploy a new version after pulling changes:
git pull
npm install
wrangler d1 migrations apply formdata-db --remote
npm run deploy