A webhook answered with HTTP 302: why retries follow, and what verification state should do meanwhile

Xây dựng tích hợp trong sandbox miễn phí trước khi chuẩn bị môi trường sản xuất.

Lựa chọn thống kê của bạn được dùng lại trên website và có thể tắt. Liên kết tài khoản cần đồng ý riêng.

A webhook answered with HTTP 302: why retries follow, and what verification state should do meanwhile

A public question describes a PayPal webhook listener that answers with HTTP 302 and then receives retries. We cannot see that application, do not represent its author and do not claim to fix PayPal or any platform. The adjacent problem in identity onboarding is concrete, though. A redirect is not an acknowledgement. When a callback is not acknowledged, the sender may retry it, or it may never be processed at all. Either way your onboarding service has to keep honest state. The reader's decision is this: when a verification callback bounces, is duplicated or never lands, what should your application record, and what must it refuse to do? This article answers that question using only the checked local synthetic example. Throughout, it separates a proposed application design from what the fixture actually implements.

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"}'

Hướng dẫn tích hợp

The problem: a redirect looks like success but is not

Many frameworks redirect unauthenticated or non-canonical requests by default. Typical causes are a login middleware, an HTTP-to-HTTPS rewrite or a trailing-slash rule. To your logs the request looked handled. To a webhook sender, a 302 is commonly not the acknowledgement it expects, so it schedules another delivery. Two risks follow. First, the same event can arrive several times once the route is fixed. Second, while the redirect persists, the event may never reach your handler, so a session sits in pending indefinitely. Proposed design, not fixture behavior: exempt the callback route from login and redirect middleware. Answer a direct success status only after the event is durably recorded. Treat any other outcome as undelivered. The fixture has no HTTP listener or redirect logic, so it cannot show this part. It shows what happens to state once events do or do not arrive.

What the fixture implements

The Identity class persists sessions and events in SQLite. The create step stores a pending session with a deadline and returns a simulated record. That record has hosted_link at an example.invalid address, link_executable false and next send_real_provider_link_only_after_authorization. The event step accepts only mode synthetic_fixture and source fixture_callback. Any other input raises PRODUCTION_SIGNATURE_VERIFIER_NOT_IMPLEMENTED, so there is no real signature verification. Event identifiers must start with fixture-. The event step also enforces the stored tenant and subject. A mismatch, or an unknown session, raises BINDING_MISMATCH, while a read of an unknown session raises SESSION_NOT_FOUND. Each event is stored under a primary key of session and event, so a repeated event returns the current state unchanged. The recover step marks pending, review or unknown sessions as expired once the deadline has passed. It does this locally and returns provider_polled false.

Worked hypothetical: a retried approval, then silence

Hypothetical scenario. A redirect bug is fixed, and the sender's queued retries arrive. In the fixture, a session is created at time 0 with a timeout of 60, so deadline is 60. The event fixture-approved arrives with status Approved and policy true. The tested output is status approved, permission_granted true, grant_count 1, live_permission_changed false, policy_version 1 and allow_action true. The same event then arrives again as a retry. The output is identical, and grant_count stays 1. A later Declined correction yields status declined, permission_granted false and grant_count 1. Now take the other path, where no callback ever arrives. Calling recover at time 61 on a fresh pending session returns status expired, permission_granted false, grant_count 0, next review_or_authorized_provider_refresh and provider_polled false. Silence never became approval.

Failure handling your application owns

Proposed design. Log every non-2xx and redirect response at the callback route as a delivery defect, not as a verification result. Persist the event before you acknowledge it, so that retries are harmless. Run recovery on a schedule against the stored deadline. After expiry, send the case to review, or to an authorized status refresh with your provider. The fixture does not perform that refresh. Treat BINDING_MISMATCH and SESSION_NOT_FOUND as rejections to investigate, never as reasons to create a new session. The fixture's permission_granted is a local demo flag. Any real permission needs your own integration, current evidence and explicit policy.

Limits of this example

The fixture accepts only synthetic callbacks. It does not validate real provider signatures, does not poll any provider and does not reproduce HTTP redirects. Its grant records are fixture data, so a synthetic permission is not live business permission. The tests cover simulated transitions, bindings, duplicate callbacks, restart and timeouts. They do not prove production success. Hosted identity trials are a separate, bounded route for eligible GB/US businesses. End-to-end action on a newly Approved real user remains unverified.

Acceptance checklist

Before you move toward a sandbox integration, confirm each of these. 1. The callback route returns no redirect and needs no login. 2. Events are stored before they are acknowledged. 3. A duplicate event leaves grant_count unchanged. 4. A Declined status after approval clears permission_granted. 5. A recover call after the deadline yields expired with provider_polled false, and does not yield approval. 6. Mismatched tenant or subject raises BINDING_MISMATCH. 7. State survives a restart on the same SQLite database. 8. Real signature checks and authorized status refresh are planned separately. Run the restart and timeout tests from the guide first.

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.sqlite

Sources 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.

Kết nối ứng dụng

Chạy ba kết quả dữ liệu mẫu · Nhận hỗ trợ tích hợp

Kết nối tài khoản sandbox miễn phí

Tính hợp lệ cơ bản của số không chứng minh quyền kiểm soát hay khả năng liên lạc và không gửi OTP. Ứng dụng của bạn chịu trách nhiệm quyết định tiếp nhận.