API ReferencePOST /v1/sites/:id/webhooks/forms

POST /v1/sites/:id/webhooks/forms

Configure where form submissions on a deployed site get delivered. Warpweb POSTs each submission to your URL with an HMAC-SHA256 signature.

Until a webhook URL is configured, form submissions deliver as plain email to the contactEmail you passed at site creation. Most API customers want the webhook path — it gives you structured JSON and signature verification.

Cost: Free to configure. Each delivery is free.

Request

curl -X POST https://api.warpweb.ai/v1/sites/8f3c2a1b/webhooks/forms \
  -H "Authorization: Bearer wwk_<your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "webhook_url": "https://api.yourapp.com/webhooks/warpweb-leads"
  }'

Body

FieldTypeRequiredDescription
webhook_urlstringyesYour HTTP(S) endpoint. Must be reachable from the public internet.
rotate_secretbooleannoDefault false. When true, regenerates the signing secret (invalidates the prior one).

Response

First configure (or rotate):

{
  "ok": true,
  "site_id": "8f3c2a1b-5d47-4c9e-b820-1f8a3e7d9c4f",
  "webhook_url": "https://api.yourapp.com/webhooks/warpweb-leads",
  "originated_via": "warpweb_public",
  "secret_issued": "whsec_a1b2c3d4e5f6...",
  "secret_note": "Copy this signing secret now — it will not be shown again. Verify Warpweb signatures with HMAC-SHA256 over `${timestamp}.${rawBody}` using this secret, where `timestamp` is the `X-Warpweb-Timestamp` request header (unix seconds) and `rawBody` is the unparsed request body bytes."
}

Subsequent idempotent re-PUT (same URL, no rotate):

{
  "ok": true,
  "site_id": "8f3c2a1b-5d47-4c9e-b820-1f8a3e7d9c4f",
  "webhook_url": "https://api.yourapp.com/webhooks/warpweb-leads",
  "originated_via": "warpweb_public",
  "secret_issued": null
}
FieldDescription
secret_issuedThe HMAC signing secret. Only returned on first configure or rotate — capture it immediately, it’s not retrievable later.
secret_noteReminder about one-time visibility, suitable for surfacing in your UI.
originated_viaHow the site was created. warpweb_public for sites built via this V1 API. Treat any non-warpweb_public value as opaque — internal-tool sites surface different strings and the set may change without notice.

Store the secret in your secret manager. You’ll use it to verify form-submission signatures on every delivery.

To rotate the secret later, call this endpoint again with "rotate_secret": true. The old secret is invalidated immediately.

Sending a test payload

Fire a synthetic test submission against your configured URL:

curl -X POST https://api.warpweb.ai/v1/sites/8f3c2a1b/webhooks/forms/test \
  -H "Authorization: Bearer wwk_<your-key>"

Returns the wire-level status (HTTP code, response body, event id) so you can verify your endpoint and signature check end-to-end before real traffic hits.

{
  "ok": true,
  "status": 200,
  "response_body": "ok",
  "event_id": "7d0b368c-b452-cf9a-1234-56789abcdef0"
}

No retries, no queue — a test failure surfaces immediately.

Picking the event type to test

There’s also a richer variant that can simulate any lifecycle event type — useful for exercising your event_id dedupe and event-type router on more than just form.submit:

curl -X POST https://api.warpweb.ai/v1/sites/8f3c2a1b/webhook/test-ping \
  -H "Authorization: Bearer wwk_<your-key>" \
  -H "Content-Type: application/json" \
  -d '{ "event_type": "site.complete" }'

Omit the body to send a webhook.test_ping envelope instead. Response shape:

{
  "ok": true,
  "status": 200,
  "response_body_preview": "ok",
  "duration_ms": 142,
  "event_type": "site.complete"
}

The body field is response_body_preview (capped at 500 chars) and the call also surfaces wall-clock duration_ms for tuning your receiver. Same 10-second timeout, same no-retry semantics as the /webhooks/forms/test variant above.

Rotating the signing secret

rotate_secret: true on the configure call above is one way to rotate. There’s also a standalone endpoint that only rotates (handy for “I leaked the secret, do this immediately, don’t touch the URL”):

curl -X POST https://api.warpweb.ai/v1/sites/8f3c2a1b/webhook/regenerate-secret \
  -H "Authorization: Bearer wwk_<your-key>"

Response:

{
  "secret": "whsec_<new-64-hex>",
  "secret_preview": "whsec_a1…cd",
  "rotated_at": "2026-05-20T14:30:00.000Z"
}

The new secret takes effect immediately. The prior secret is invalidated — in-flight deliveries already in our retry queue use the new secret on the next attempt.

What gets delivered

See Webhooks → Form Submissions for the full payload contract, signature format, and retry behavior.

Errors

StatusBodyCause
400{ "error": "webhook_url is required and must be http(s)://" }URL missing or malformed.
403{ "error": "Forbidden" }Caller doesn’t own this site.
404{ "error": "Site not found" }No site with that ID belongs to your account.