Complete reference for all Wrangler bindings, secrets, queue settings, and D1 schema used by formdata.dev.
These bindings are defined in wrangler.jsonc and available to the Worker at runtime via env.
| Binding | Type | Name/ID | Description |
|---|---|---|---|
DB |
D1 Database | formdata-db |
Stores organizations, forms, destinations, API keys |
FORM_KV |
KV Namespace | — | Edge cache for form config (keyed by public key) |
DELIVERY_QUEUE |
Queue Producer | formdata-delivery |
Enqueues one message per destination per submission |
MCP_AGENT |
Durable Object | FormDataMcpAgent |
MCP agent for AI-driven tenant management |
Secrets are set via wrangler secret put <NAME> and are not stored in source control.
| Secret | Required | Description |
|---|---|---|
CF_ZONE_ID |
No | Cloudflare Zone ID for subdomain provisioning |
CF_API_TOKEN |
No | Cloudflare API token with Zone > Custom Hostnames > Edit permission |
CAPTCHA_VERIFY_URL |
No | URL for captcha token verification (e.g., Cloudflare Turnstile verify endpoint) |
Individual form captcha secrets (captchaSecret) are stored in D1 per form, not as Worker secrets. The CAPTCHA_VERIFY_URL is the global verification endpoint used by all forms.
Configured in the queues.consumers section of wrangler.jsonc:
| Setting | Value | Description |
|---|---|---|
queue |
formdata-delivery |
Queue name |
max_batch_size |
10 |
Max messages processed per invocation |
max_retries |
5 |
Retries before moving to DLQ |
dead_letter_queue |
formdata-delivery-dlq |
Queue for permanently failed messages |
retry_delay |
30 |
Seconds between retries |
max_batch_size (up to 100) if you have high submission volume and want fewer Worker invocationsmax_retries if your SMTP server or webhook endpoint has frequent transient failuresretry_delay for faster retry cycles (minimum 0 seconds)The migrations section in wrangler.jsonc declares the Durable Object classes:
Each migration tag must be unique. If you add new Durable Object classes in the future, add a new migration entry with a new tag (e.g., v2). Never modify existing migration entries.
nodejs_compat is required for cloudflare:sockets (used by the SMTP connector) and other Node.js APIsThe database schema is defined in migrations/0000_init.sql. Below is the complete schema reference.
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
TEXT |
PRIMARY KEY NOT NULL |
UUID |
name |
TEXT |
NOT NULL |
Organization display name |
slug |
TEXT |
NOT NULL UNIQUE |
URL slug, used for subdomain |
owner_email |
TEXT |
NOT NULL |
Account owner email |
cf_custom_hostname_id |
TEXT |
nullable | Cloudflare Custom Hostname ID |
created_at |
TEXT |
NOT NULL |
ISO 8601 timestamp |
updated_at |
TEXT |
NOT NULL |
ISO 8601 timestamp |
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
TEXT |
PRIMARY KEY NOT NULL |
UUID |
organization_id |
TEXT |
NOT NULL, FK → organizations(id) ON DELETE CASCADE |
Owning organization |
key_hash |
TEXT |
NOT NULL UNIQUE |
SHA-256 hash of the tenant key |
label |
TEXT |
NOT NULL |
Human label (e.g., primary, rotated-2026-...) |
created_at |
TEXT |
NOT NULL |
ISO 8601 timestamp |
revoked_at |
TEXT |
nullable | Set on key rotation; non-null means revoked |
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
TEXT |
PRIMARY KEY NOT NULL |
UUID |
organization_id |
TEXT |
NOT NULL, FK → organizations(id) ON DELETE CASCADE |
Owning organization |
name |
TEXT |
NOT NULL |
Display name |
slug |
TEXT |
NOT NULL |
URL-safe identifier |
public_key |
TEXT |
NOT NULL UNIQUE |
pk_ prefixed key for ingestion URL |
allowed_origins |
TEXT |
NOT NULL |
JSON array of allowed origin URLs |
verify_captcha |
INTEGER |
NOT NULL DEFAULT 0 |
1 = captcha required |
captcha_secret |
TEXT |
nullable | Captcha verification secret |
is_enabled |
INTEGER |
NOT NULL DEFAULT 1 |
1 = accepting submissions |
created_at |
TEXT |
NOT NULL |
ISO 8601 timestamp |
updated_at |
TEXT |
NOT NULL |
ISO 8601 timestamp |
| Column | Type | Constraints | Description |
|---|---|---|---|
id |
TEXT |
PRIMARY KEY NOT NULL |
UUID |
form_id |
TEXT |
NOT NULL, FK → forms(id) ON DELETE CASCADE |
Parent form |
type |
TEXT |
NOT NULL |
webhook, smtp, or google_sheets_webhook |
config |
TEXT |
NOT NULL |
JSON config object (varies by type) |
is_enabled |
INTEGER |
NOT NULL DEFAULT 1 |
1 = active |
created_at |
TEXT |
NOT NULL |
ISO 8601 timestamp |
updated_at |
TEXT |
NOT NULL |
ISO 8601 timestamp |
| Index | Table | Column(s) |
|---|---|---|
idx_organizations_slug |
organizations |
slug |
idx_forms_public_key |
forms |
public_key |
idx_forms_org |
forms |
organization_id |
idx_org_api_keys_org |
organization_api_keys |
organization_id |
idx_destinations_form |
destinations |
form_id |
Each form's config is stored in KV under the key form:<public_key>. The value is a JSON object:
KV is synced automatically after every admin mutation (create, update, delete) via the syncFormToKV and deleteFormFromKV functions. The ingestion endpoint reads exclusively from KV and never touches D1.