feat(jotform): trigger a workflow on every new form submission - #6802
Conversation
Jotform's only webhook event is a new submission, so the block gets one
trigger. Deploying it registers the callback on the form through the API
and undeploying removes it again.
Two things about this provider needed handling:
Jotform posts submissions as multipart/form-data, which the shared webhook
body parser did not read — the delivery died as a 400 before any handler
saw it. The parser now flattens a multipart body the same way it already
flattens a urlencoded one, reducing an uploaded part to its filename so a
stray file cannot inflate the execution input.
The form's webhooks are identified by their position in the form's webhook
map, so an id captured at registration goes stale the moment any other
webhook on that form is removed. Nothing persists it; cleanup re-resolves
the id by matching the callback URL. Registration checks the same way,
because Jotform answers a rejected request with the unchanged list rather
than an error.
Answers are exposed as the parsed `rawRequest` rather than re-keyed by
question label — the labels are not unique, and the payload shape is only
documented as the raw q{qid}_{slug} map.
The trigger's region field is named `apiRegion` so it does not collide
with the block's own advanced-mode `region`.
…olerant
Validated the trigger against Jotform's API reference and a captured
delivery (zulip's multipart fixture), which confirmed every mapped field —
formID, submissionID, formTitle, username, ip, type, pretty, rawRequest —
and turned up three things worth correcting.
Jotform keeps a form's webhooks as a plain list and does not treat the URL
as a key, so posting one it already holds leaves the form delivering every
submission twice. Registration now consults the list first and only posts
when the URL is absent. The documented POST sample returns the new entry as
"0", renumbering the rest, which is further reason nothing persists an id.
URL matching no longer lets a trailing slash decide the outcome. Jotform
stores the URL verbatim in every sample seen, but an exact match failing
would hard-fail deploy, and Pipedream's client normalizes the same way.
The rawRequest description claimed the field holds the submitted answers.
A real payload also carries slug, buildDate, submitSource and
jsExecutionTracker, and a file answer appears under the bare slugified
label as upload URLs rather than under a q{qid}_ key — which is also why
filtering to q-prefixed keys would silently drop file answers.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryMedium Risk Overview Shared webhook ingestion now parses A new Jotform webhook provider maps multipart payloads into trigger outputs (including parsed Reviewed by Cursor Bugbot for commit c50c11e. Configure here. |
Greptile SummaryThe PR adds a Jotform submission trigger, including multipart payload parsing and external webhook registration lifecycle management. The latest fix protects the shared callback during redeployment by checking for an active replacement before cleaning up the retired registration.
Confidence Score: 5/5The PR appears safe to merge because the previously reported callback deletion failure is addressed and no blocking failure remains. No blocking failure remains; activation commits the replacement deployment version before retired-webhook cleanup, allowing the teardown guard to detect the active replacement and preserve its callback.
|
| Filename | Overview |
|---|---|
| apps/sim/lib/webhooks/providers/jotform.ts | Implements Jotform payload normalization and external subscription lifecycle management; the redeployment teardown guard now preserves callbacks used by an active replacement. |
| apps/sim/lib/webhooks/providers/jotform.test.ts | Covers payload mapping, idempotent registration, regional endpoints, cleanup behavior, and preservation of callbacks shared with active deployments. |
| apps/sim/lib/webhooks/processor.ts | Extends shared webhook request parsing to flatten multipart form data while reducing uploaded parts to filenames. |
| apps/sim/triggers/jotform/webhook.ts | Defines the Jotform submission trigger configuration, credentials, setup instructions, and output contract. |
| apps/sim/blocks/blocks/jotform.ts | Exposes the new Jotform trigger through the existing integration block. |
| apps/sim/triggers/registry.ts | Registers the new Jotform trigger in the shared trigger registry. |
Sequence Diagram
sequenceDiagram
participant Deploy as Deployment lifecycle
participant New as Replacement webhook row
participant Jotform as Jotform API
participant Old as Retired webhook row
participant DB as Deployment state
Deploy->>New: Prepare candidate registration
New->>Jotform: Reuse existing callback
Deploy->>DB: Activate replacement generation
DB-->>Deploy: Commit active deployment version
Deploy->>Old: Clean up retired generation
Old->>DB: Check for active row using same form and callback
DB-->>Old: Replacement still uses callback
Old-->>Jotform: Skip callback deletion
Reviews (2): Last reviewed commit: "fix(jotform): keep the callback when an ..." | Re-trigger Greptile
Redeploying prepares the replacement webhook row alongside the live one and a workflow keeps its path across deployments, so both rows resolve to a single callback on a single form. Registration adopts the callback already present instead of posting a duplicate, which left the retired row's cleanup deleting the one the new row had just adopted — the trigger went silent after a redeploy that changed the trigger config. Teardown now skips when another webhook row belonging to an active deployment resolves to the same form and callback URL, matching how the Telegram handler skips deleteWebhook while an active deployment still uses the same bot. A genuine undeploy has no such row and still cleans up.
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c50c11e. Configure here.
Summary
jotform_webhooktrigger — new submission is Jotform's only webhook event — wired into the Jotform block, trigger registry, and provider registrymultipart/form-data. Jotform posts submissions that way, andparseWebhookBodyonly handled JSON and urlencoded, so every delivery would have died as a 400 before reaching a handler. An uploaded part is reduced to its filename so a stray file cannot inflate the execution input"0"), so create and delete both re-resolve the id by matching the callback URL, ignoring a trailing slashNotes
Outputs are mapped from fields confirmed against Jotform's API reference and a captured delivery:
formID,submissionID,formTitle,username,ip,type,pretty,rawRequest.Answers are exposed as the parsed
rawRequestrather than re-keyed by question label. Labels are not unique, and filtering toq{qid}_keys would silently drop file answers — those land under the bare slugified label as upload URLs.Jotform sends no signature or shared secret, so there is no
verifyAuth; the webhook path is the credential, as with other unsigned providers.Type of Change
Testing
bun run type-check,bun run lint,bun run check:audits(29/29), andcheck-block-registryagainst staging all passlib/webhooks+triggers+blocks+tools/jotformsuite: 1622 passingChecklist