Webhooks on localhost: test verification callbacks without a public endpoint
A public question asks how to receive webhooks while developing on localhost. A hosted provider cannot reach 127.0.0.1, so during development the callback may never arrive. We do not represent the person who asked, and we do not claim to fix any platform. For identity onboarding the adjacent decision is concrete: what should your application do while it waits for a callback that cannot reach it? This article uses only the checked local synthetic example to show one answer. Persist a pending session with a deadline, feed synthetic callbacks directly into the handler, and let a missing callback expire locally. A missing callback must never become approval.
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
On localhost, an unreachable callback looks the same as a slow provider: the session stays pending. The risky shortcut is to approve users manually or to treat a timeout as success so development can continue. The decision in front of you is narrower. Do you test state transitions locally, separately from network delivery? Or do you need a real tunnelled sandbox before your logic can be trusted? The fixture supports the first path. It exercises your state handling with no public URL, no credentials and no network calls. It does not test real delivery, signatures or provider retries.
What the fixture implements
The Identity class stores sessions and events in SQLite. create() requires identifiers that start with fixture- and accepts a timeout between 1 and 3600 seconds. It stores status pending and sets deadline to now plus the timeout. It returns hosted_link pointing at example.invalid, with link_executable false. event() accepts only mode synthetic_fixture and source fixture_callback. Any other mode or source raises PRODUCTION_SIGNATURE_VERIFIER_NOT_IMPLEMENTED. Event IDs must also start with fixture-. recover() marks a pending, review or unknown session as expired once deadline has passed. It returns provider_polled false. It expires the session locally and never queries a provider.
Worked hypothetical scenario
Hypothetically, a developer runs onboarding on a laptop. A session is created at now=0 with timeout 60, so deadline is 60. No callback can reach localhost. The developer does not wait for the network. Instead, they inject a synthetic event, fixture-approved with status Approved and policy true. The read returns status approved, permission_granted true, grant_count 1, policy_version 1, allow_action true and live_permission_changed false. Sending the same event again returns the identical record, so grant_count stays 1. A later Declined event returns status declined, permission_granted false, with grant_count still 1. In a second run, no callback arrives and recover is called at now=61. The result is status expired, permission_granted false, grant_count 0, next review_or_authorized_provider_refresh and provider_polled false.
Failure handling
An event for an unknown session, or for the wrong tenant or subject, raises BINDING_MISMATCH. Reading a session that does not exist raises SESSION_NOT_FOUND. An unrecognised status moves the session to unknown, never to approved. An event that arrives after the deadline produces expired. Events on sessions that are already finalised do not reopen them; the one exception is a Declined correction to an approved session. Your application should treat each of these as a reason for review, not as a retry that eventually grants access. Even a granted result is a local fixture record: live_permission_changed is always false.
Proposed application design versus fixture behavior
Everything above is implemented fixture behavior. The following is proposed design that you would have to build and verify yourself. First, expose a development route, or a tunnel you are authorized to use, only for sandbox traffic. Second, add provider-specific signature validation. Third, perform bounded, authorized status reconciliation when a deadline passes, rather than only expiring the session. Finally, decide business permissions through your own rules. The fixture's decide() and revise_policy() methods illustrate explicit, versioned approval, but synthetic permission is not live business permission. The downloadable example has four branches for phone checks plus the identity flow. It is not the same thing as the anonymous three-scenario HTTP trial.
Acceptance checklist
Run the restart and timeout tests named in the guide. Restart with the same SQLite database and confirm the pending state survives. Confirm a duplicate event leaves grant_count at 1. Confirm recover after the deadline returns expired with provider_polled false. Confirm that non-fixture sources are rejected. Confirm that no code path turns a missing callback into approved. Before any real sandbox, record what remains unproven: signature verification, real delivery and retries, reconciliation, and real user permission. Then decide whether a saved sandbox request fits your task.
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.