The HTTP response was sent, but the workflow is still running: keep verification state honest
A public n8n issue describes a webhook workflow that has already returned a successful HTML response while its execution, which includes a PostgreSQL node, still shows as running. We cannot diagnose or fix that vendor issue, and we do not represent anyone affected by it. It does raise an adjacent question for teams building identity onboarding. When the HTTP layer says done but the work behind it has not finished, what should your application believe about a verification session? This article answers that question with the synthetic verification_delivery fixture. The goal is to help you decide whether a sandbox integration fits your task.
Bài này hiện bằng tiếng Anh; bản dịch đang chờ cập nhật.
curl https://gridzen.ai/console/api/quickstart/trial \
-H 'Content-Type: application/json' \
-d '{"task": "identity_onboarding", "scenario": "success"}'The problem and your decision
A 200 response tells the sender only that a request was received. It does not tell you that your database write committed, that a decision was recorded, or that a permission changed. If a callback handler responds and then stalls, three outcomes are possible: the session state may be stale, the update may be partial, or a retried callback may arrive. Your decision is where truth lives. Our recommendation is to keep it in persisted session state with a deadline, and never in the HTTP outcome or in the existence of an execution. A session that is stuck or missing a callback must never turn into approval.
What the fixture actually implements
The downloadable example stores sessions and events in SQLite. The create step records a pending session with a deadline; the default deadline is 60. The event step does four things. It rejects anything that is not a synthetic fixture_callback, raising PRODUCTION_SIGNATURE_VERIFIER_NOT_IMPLEMENTED. It checks the tenant and subject binding. It deduplicates by event ID within a single immediate transaction. It normalizes the status. The recover step marks pending, review or unknown sessions as expired once the deadline has passed. It runs locally and does not poll any provider. Every read returns live_permission_changed: false. A granted permission in the fixture is a local record, not a business permission.
Worked hypothetical scenario
Imagine your callback handler replied quickly, but its database step hung. You restart the service against the same SQLite file. Creating the session returns state simulated, hosted_link under example.invalid, link_executable false, and next send_real_provider_link_only_after_authorization. A synthetic Approved event with an explicit policy returns status approved, deadline 60, permission_granted true and grant_count 1. If the sender retries the same event, the result is identical: grant_count is still 1. A later Declined correction returns status declined and permission_granted false, while grant_count stays 1. In a separate session where no callback ever lands, recover at now=61 returns: - status expired - permission_granted false - grant_count 0 - next review_or_authorized_provider_refresh - provider_polled false
Failure handling you own
The fixture reports two errors. An event for an unknown session raises BINDING_MISMATCH, and reading an unknown session raises SESSION_NOT_FOUND. Treat both as signals to investigate, not as reasons to retry blindly. Proposed application design, which is not implemented here: - Commit state before you acknowledge, or acknowledge only once the event is persisted idempotently. - Alert on sessions that stay pending close to their deadline. - Use bounded retries. - Route expired sessions to review or to an authorized status refresh. A production system also needs provider-specific signature validation and bounded status reconciliation. The fixture does not establish either.
Limits worth stating plainly
Fixture events accept only synthetic callbacks, never real signatures. Recover expires sessions locally and never contacts a provider. A synthetic grant is not live business permission, and Approved is not unconditional permission in any setting. The guide also offers a short anonymous HTTP trial, which is a separate thing from the downloadable example and its branch tests. Passing tests prove only simulated transitions. They do not prove production success. Live route availability requires separate confirmation.
Acceptance checklist
- The restart and timeout recovery tests pass when the same database is reused. - A duplicate event leaves grant_count unchanged. - A Declined correction sets permission_granted to false. - An expired session shows provider_polled false and points to review. - Events with unknown bindings are rejected. - No code path treats an HTTP 200 or a running execution as approval. - Your own design document names who performs the authorized refresh and who performs review.
Run the checked synthetic example
Download and extract https://gridzen.ai/downloads/verification-delivery.zip, enter verification_delivery, then run with Python 3.10+. This local example makes no network calls. Use a fresh database for an independent identity run.
python3 delivery.py identity --db /tmp/gridzen-article-fixture.sqliteSources and scope
https://gridzen.ai/guides/missing-verification-webhook/ https://gridzen.ai/guides/current-capabilities/ Based on the checked source snapshot and local fixture; proposed application rules are design advice, not proven production behavior. Written with AI assistance and reviewed independently.
Thử riêng ba kết quả sandbox Tạo tài khoản để tiếp tục tích hợp
Tải xuống không chứng minh đã chạy thành công. Ví dụ cục bộ và sandbox trực tuyến được thử riêng.