Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions api-features/webhooks-events.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,17 @@

## Event Categories

The platform and Client ID webhook catalog includes **14 event types** across five categories:
The platform and Client ID webhook catalog includes **10 event types** across six categories:

| Category | Events |
| --- | --- |
| **Payment** (core) | `payment.confirmed`, `payment.partial`, `payment.failed`, `payment.refunded` |
| **Payment** (Client ID-scoped) | `payment.confirmed.client_id`, `payment.partial.client_id` |
| **Payment** (Checkout-scoped) | `payment.confirmed.checkout`, `payment.partial.checkout` |
| **Processing** (crypto-to-fiat) | `payment.processing` (with `subStatus`) |
| **Request** | `request.recurring` |
| **Compliance / Bank** | `compliance.updated`, `payment_detail.updated` |
| **Secure Payment Page** (payer funnel) | `secure_payment.user_event` (with `userEvent`) |
| **Secure Payment** (payer-wallet allowlist) | `secure_payment.access_rejected` |

Check warning on line 21 in api-features/webhooks-events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-features/webhooks-events.mdx#L21

Did you really mean 'allowlist'?

The `.client_id` and `.checkout` variants are emitted in addition to the base `.confirmed` / `.partial` events when the request was created via a Client ID or as a checkout / secure payment, respectively. They include extra metadata (`clientId`, `origin`).

<Note>
Payment webhook payloads include `payerAddress`, the address used to make the payment, and `payerEoaAddress`, the payer's connected wallet address. These can differ when a smart account is used. Both are `null` when unavailable. See the [Webhooks reference](/api-reference/webhooks) for the full payload schema.
</Note>
Expand All @@ -49,7 +45,7 @@
## Key Features

### Reliability
- **Idempotency support:** Use `x-request-network-delivery` header for duplicate detection

Check warning on line 48 in api-features/webhooks-events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-features/webhooks-events.mdx#L48

Did you really mean 'Idempotency'?
- **Delivery confirmation:** Monitor `x-request-network-retry-count` header to track attempts

### Security
Expand All @@ -59,7 +55,7 @@

### Development Tools
- **Test deliveries:** Fire test events via `POST /v1/webhook/test` (Auth API) — see [Webhooks reference](/api-reference/webhooks#testing)
- **ngrok integration:** Receive webhooks locally during development

Check warning on line 58 in api-features/webhooks-events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-features/webhooks-events.mdx#L58

Did you really mean 'ngrok'?
- **Comprehensive logging:** Request API logs all delivery failures with attempt details

## Common Use Cases
Expand All @@ -68,7 +64,7 @@
- **Order fulfillment:** Release goods or services immediately after payment confirmation
- **Subscription management:** Handle `request.recurring` for automatic billing renewals
- **Compliance workflows:** Update user permissions when `compliance.updated` shows KYC approval
- **Real-time dashboards:** Display live payment status using `payment.processing` subStatus values

Check warning on line 67 in api-features/webhooks-events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-features/webhooks-events.mdx#L67

Did you really mean 'subStatus'?
- **Payer-funnel visibility:** Track wallet connection and signature progress on the Secure Payment Page via `secure_payment.user_event`
- **Payer-wallet monitoring:** Detect attempts by wallets that are not allowed to pay via `secure_payment.access_rejected`

Expand Down
22 changes: 2 additions & 20 deletions api-reference/webhooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -62,34 +62,16 @@

| Event | Description | Context | Primary Use |
|-------|-------------|---------|-------------|
| `payment.confirmed` | Payment fully completed and settled | After blockchain confirmation | Complete fulfillment, release goods |

Check warning on line 65 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L65

Did you really mean 'blockchain'?
| `payment.partial` | Partial payment received for request | Installments, partial orders | Update balance, allow additional payments |
| `payment.failed` | Payment execution failed | Recurring payments, cross-chain transfers | Notify failure, retry logic, pause subscriptions |
| `payment.refunded` | Payment has been refunded to payer | Cross-chain payment failures, refund scenarios | Update order status, notify customer |

### Payment Events (Client ID-scoped)

Emitted **in addition to** the core events when the originating request was created with a Client ID. Payload includes extra `clientId` and `origin` fields.

| Event | Description |
|-------|-------------|
| `payment.confirmed.client_id` | Same as `payment.confirmed`, scoped to a Client ID |
| `payment.partial.client_id` | Same as `payment.partial`, scoped to a Client ID |

### Payment Events (Checkout / Secure Payment-scoped)

Emitted **in addition to** the core events when the request was created via a Secure Payment / checkout flow.

| Event | Description |
|-------|-------------|
| `payment.confirmed.checkout` | Same as `payment.confirmed`, originating from a Secure Payment link |
| `payment.partial.checkout` | Same as `payment.partial`, originating from a Secure Payment link |

### Processing Events
Comment thread
greptile-apps[bot] marked this conversation as resolved.

| Event | Description | Context | Primary Use |
|-------|-------------|---------|-------------|
| `payment.processing` | Crypto-to-fiat payment in progress | **subStatus values:** initiated, pending_internal_assessment, ongoing_checks, sending_fiat, fiat_sent, bounced | Track crypto-to-fiat payment status, update UI |

Check warning on line 74 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L74

Did you really mean 'subStatus'?

Check warning on line 74 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L74

Did you really mean 'pending_internal_assessment'?

Check warning on line 74 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L74

Did you really mean 'ongoing_checks'?

Check warning on line 74 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L74

Did you really mean 'sending_fiat'?

Check warning on line 74 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L74

Did you really mean 'fiat_sent'?

### Request Events

Expand All @@ -101,15 +83,15 @@

| Event | Description | Context | Primary Use |
|-------|-------------|---------|-------------|
| `compliance.updated` | KYC or agreement status changed | **kycStatus values:** not_started, pending, approved, rejected, retry_required<br/>**agreementStatus values:** not_started, pending, completed, rejected, failed | Update user permissions, notify status |

Check warning on line 86 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L86

Did you really mean 'kycStatus'?

Check warning on line 86 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L86

Did you really mean 'not_started'?

Check warning on line 86 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L86

Did you really mean 'retry_required'?

Check warning on line 86 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L86

Did you really mean 'agreementStatus'?

Check warning on line 86 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L86

Did you really mean 'not_started'?
| `payment_detail.updated` | Bank account verification status updated | States: approved, failed, pending | Enable fiat payments, update profiles |

### Secure Payment Events

| Event | Description | Context | Primary Use |
|-------|-------------|---------|-------------|
| `secure_payment.user_event` | Payer progressed through a step of the Secure Payment Page | **userEvent values:** wallet_connected, payment_sent_to_wallet, payment_approved_in_wallet | Real-time payer-funnel visibility, drop-off analytics |

Check warning on line 93 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L93

Did you really mean 'userEvent'?

Check warning on line 93 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L93

Did you really mean 'wallet_connected'?

Check warning on line 93 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L93

Did you really mean 'payment_sent_to_wallet'?

Check warning on line 93 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L93

Did you really mean 'payment_approved_in_wallet'?
| `secure_payment.access_rejected` | A wallet not on a payment's payer-wallet allowlist attempted to access or pay it | Incoming Secure Payments with `allowedPayerAddresses` | Monitor rejected payer attempts |

Check warning on line 94 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L94

Did you really mean 'allowlist'?

Sent to the same registered webhook endpoints as every other event — same Client ID scoping, `x-request-network-signature` HMAC verification, delivery headers, timeout, and 1s/5s/15s retry semantics described elsewhere on this page.

Expand All @@ -119,7 +101,7 @@
|-------------|---------|
| `wallet_connected` | The payer successfully connected a wallet on the secure payment page |
| `payment_sent_to_wallet` | The payment transaction was handed to the payer's wallet for signature |
| `payment_approved_in_wallet` | The payer approved/signed the payment in their wallet. `properties` includes the submission id (e.g. tx hash / user-operation hash) |

Check warning on line 104 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L104

Did you really mean 'tx'?

<Note>
`securePaymentToken` is the platform's correlation key, returned when the secure payment was created. `requestId` is present only when exactly one request is linked to the secure payment (see `requestIds` for the full list). `timestamp` is server-stamped on receipt. `occurredAt` and `properties` are **client-reported telemetry from the payer's browser** — useful for analytics, but not authoritative.
Expand All @@ -131,7 +113,7 @@

### Payer-wallet access rejections

`secure_payment.access_rejected` is generated server-side when a wallet that is not on an incoming payment's `allowedPayerAddresses` allowlist tries to access or pay it. It is not emitted for KYT decisions. See [Restrict payer wallets](/use-cases/restrict-payer-wallets) to configure the allowlist.

Check warning on line 116 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L116

Did you really mean 'allowlist'?

Check warning on line 116 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L116

Did you really mean 'allowlist'?

The event is sent to the payment's platform-wide and Client ID webhooks, not to an orchestrator webhook. Repeated attempts by the same wallet on the same payment are normally suppressed for 10 minutes. If every configured webhook endpoint fails, the next access attempt can trigger another notification.

Expand All @@ -141,7 +123,7 @@
Every webhook includes an HMAC SHA-256 signature in the `x-request-network-signature` header:

```javascript
import crypto from "node:crypto";

Check warning on line 126 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L126

Did you really mean 'crypto'?

function verifyWebhookSignature(rawBody, signature, secret) {
const expectedSignature = crypto
Expand Down Expand Up @@ -228,9 +210,9 @@
- `requestId` / `requestID`: Unique identifier for the payment request
- `paymentReference`: Short reference, also unique to a request, used to link payments to the request
- `timestamp`: ISO 8601 formatted event timestamp
- `paymentProcessor`: Either `request-network` (crypto) or `request-tech` (fiat)

Check warning on line 213 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L213

Did you really mean 'crypto'?
- `payerAddress`: Resolved payer wallet — the on-chain sender for plain direct payments, or the resolved payer for recurring and intent-based flows (Secure Payment Page, LiFi, Safe, ERC-4337, multicall). `null` when it cannot be determined. Included on `payment.confirmed` and `payment.partial` events (and their `.client_id` / `.checkout` variants).
- `payerEoaAddress`: The payer's connected wallet address. It can differ from `payerAddress` when a smart account is used. `null` when unavailable. Included on `payment.confirmed` and `payment.partial` events (and their `.client_id` / `.checkout` variants).
- `payerAddress`: Resolved payer wallet — the on-chain sender for plain direct payments, or the resolved payer for recurring and intent-based flows (Secure Payment Page, LiFi, Safe, ERC-4337, multicall). `null` when it cannot be determined. Included on `payment.confirmed` and `payment.partial` events.

Check warning on line 214 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L214

Did you really mean 'multicall'?
- `payerEoaAddress`: The payer's connected wallet address. It can differ from `payerAddress` when a smart account is used. `null` when unavailable. Included on `payment.confirmed` and `payment.partial` events.

### Payment Confirmed
```json
Expand Down Expand Up @@ -368,7 +350,7 @@
| Field | Description |
|-------|-------------|
| `requestId` | The request the wallet tried to access. |
| `attemptedPayerWalletAddress` | The rejected wallet address. EVM addresses are lowercased; TRON addresses keep their original case. |

Check warning on line 353 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L353

Did you really mean 'lowercased'?
| `timestamp` | When Request Network emitted the event. |

Use `POST /v1/webhook/test` with `{ "eventType": "secure_payment.access_rejected" }` to test this event without a rejected access attempt.
Expand Down Expand Up @@ -537,7 +519,7 @@
## Best Practices

### Error Handling
- **Implement idempotency:** Use delivery IDs to prevent duplicate processing

Check warning on line 522 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L522

Did you really mean 'idempotency'?
- **Graceful degradation:** Handle unknown event types without errors

### Performance
Expand All @@ -558,7 +540,7 @@
- Confirm the webhook is `active` via `GET /v1/webhook` (toggle with `PUT /v1/webhook/:id`)

### Debugging Tips
- Use ngrok request inspector to see raw webhook data

Check warning on line 543 in api-reference/webhooks.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

api-reference/webhooks.mdx#L543

Did you really mean 'ngrok'?
- Monitor retry counts in headers to identify issues
- Fire test deliveries via `POST /v1/webhook/test`

Expand Down
6 changes: 1 addition & 5 deletions use-cases/quickstart.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "Quickstart"
description: "End-to-end walkthrough — sign in to the Dashboard, create a payment destination, register a webhook, and create a hosted payment link."

Check warning on line 3 in use-cases/quickstart.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/quickstart.mdx#L3

Did you really mean 'walkthrough'?
---

This guide walks through the canonical flow for receiving payments via Request Network: creating a payment destination and Client ID in the Dashboard, registering a webhook for payment notifications, and creating a payment link. Every other use-case page links back to specific steps here.
Expand Down Expand Up @@ -235,7 +235,7 @@
| Header | Description |
| --- | --- |
| `x-request-network-signature` | HMAC-SHA256 of the raw JSON body, signed with your webhook secret |
| `x-request-network-delivery` | Unique delivery ID — use as an idempotency key |

Check warning on line 238 in use-cases/quickstart.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/quickstart.mdx#L238

Did you really mean 'idempotency'?
| `x-request-network-retry-count` | Retry attempt number (`0`–`3`) |
| `x-request-network-test` | `true` only for test deliveries via `/v1/webhook/test` |

Expand All @@ -259,7 +259,7 @@

### Webhook events for payment links

When a payer completes a payment on a payment link you created, your webhook receives a `payment.confirmed` event (or `payment.partial` for partial payments). For Client ID-scoped variants you'll also receive `payment.confirmed.client_id` / `payment.partial.client_id` with extra `clientId` and `origin` fields.
When a payer completes a payment on a payment link you created, your webhook receives a `payment.confirmed` event (or `payment.partial` for partial payments).

**Example `payment.confirmed` payload:**

Expand Down Expand Up @@ -293,7 +293,7 @@
| `totalAmountPaid` | Cumulative amount paid so far |
| `expectedAmount` | The total amount expected |
| `txHash` | On-chain transaction hash |
| `network` | The blockchain network |

Check warning on line 296 in use-cases/quickstart.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/quickstart.mdx#L296

Did you really mean 'blockchain'?
| `currency` | The token used for payment |

### All supported webhook events
Expand All @@ -302,18 +302,14 @@
| --- | --- |
| `payment.confirmed` | Payment fully confirmed |
| `payment.partial` | Partial payment received |
| `payment.confirmed.client_id` | Client ID-scoped variant of `payment.confirmed` |
| `payment.partial.client_id` | Client ID-scoped variant of `payment.partial` |
| `payment.confirmed.checkout` | Secure-payment-scoped variant of `payment.confirmed` |
| `payment.partial.checkout` | Secure-payment-scoped variant of `payment.partial` |
| `payment.failed` | Payment failed |
| `payment.refunded` | Payment refunded |
| `payment.processing` | Offramp processing started |

Check warning on line 307 in use-cases/quickstart.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/quickstart.mdx#L307

Did you really mean 'Offramp'?
| `request.recurring` | A recurring request fired |
| `payment_detail.updated` | Payment detail metadata changed |
| `compliance.updated` | Compliance status changed |
| `secure_payment.user_event` | Payer progressed through a Secure Payment Page step (`userEvent`: `wallet_connected`, `payment_sent_to_wallet`, `payment_approved_in_wallet`) — funnel telemetry, not a settlement signal |
| `secure_payment.access_rejected` | A wallet not on a Secure Payment's payer-wallet allowlist tried to access or pay it |

Check warning on line 312 in use-cases/quickstart.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/quickstart.mdx#L312

Did you really mean 'allowlist'?

<Note>
`secure_payment.user_event` is best-effort browser telemetry. The payer's browser can fail to reach the API, and retries begin only once the API has accepted the event — so a missing event is not evidence that the payer skipped the step. Use `payment.confirmed` for settlement and reconciliation.
Expand Down
10 changes: 1 addition & 9 deletions use-cases/webhook-reconciliation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,20 @@

**Audience:** any backend integrating Request Network where payment events drive state changes downstream.

## The 14 events
## The 10 events

| Category | Event | When it fires |
| --- | --- | --- |
| Payment (core) | `payment.confirmed` | Payment fully settled on-chain |
| | `payment.partial` | Partial payment received, more expected |
| | `payment.failed` | Payment execution failed (recurring, cross-chain) |
| | `payment.refunded` | Payment refunded to payer |
| Payment (Client ID) | `payment.confirmed.client_id` | Same as `payment.confirmed`, request was created via Client ID — payload includes `clientId` and `origin` |
| | `payment.partial.client_id` | Client ID-scoped partial |
| Payment (Checkout) | `payment.confirmed.checkout` | Same as `payment.confirmed`, request originated from a Secure Payment link |
| | `payment.partial.checkout` | Secure Payment-scoped partial |
| Processing | `payment.processing` | Crypto-to-fiat offramp in progress (with detailed `subStatus`) |

Check warning on line 20 in use-cases/webhook-reconciliation.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/webhook-reconciliation.mdx#L20

Did you really mean 'offramp'?
| Request | `request.recurring` | A new recurring billing cycle fired |
| Compliance | `compliance.updated` | KYC or agreement status changed |
| Bank details | `payment_detail.updated` | Bank account verification status changed |
| Secure Payment Page | `secure_payment.user_event` | Payer progressed through a step of the Secure Payment Page (`userEvent`: `wallet_connected`, `payment_sent_to_wallet`, `payment_approved_in_wallet`) — funnel telemetry, **not** a settlement signal |
| Secure Payment | `secure_payment.access_rejected` | A wallet not on a payer-wallet allowlist tried to access or pay the payment |

Check warning on line 25 in use-cases/webhook-reconciliation.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/webhook-reconciliation.mdx#L25

Did you really mean 'allowlist'?

`secure_payment.user_event` is best-effort browser telemetry, and the stream is intentionally incomplete: the payer's browser can fail to reach the API, and retries begin only once the API has accepted the event. A missing event is not evidence that the payer skipped the step, so do not drive drop-off, notification, or reconciliation logic off its absence.

Expand All @@ -52,7 +48,7 @@

## Handler — reference implementation

A signature-verifying Express handler. It verifies against the **raw** body, uses constant-time comparison, passes the delivery ID to business handlers as their idempotency key, and lets Request Network retry a failed handler.

Check warning on line 51 in use-cases/webhook-reconciliation.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/webhook-reconciliation.mdx#L51

Did you really mean 'idempotency'?

```typescript
import { createHmac, timingSafeEqual } from "node:crypto";
Expand Down Expand Up @@ -104,14 +100,10 @@
async function handleEvent(event: any, deliveryId: string) {
switch (event.event) {
case "payment.confirmed":
case "payment.confirmed.client_id":
case "payment.confirmed.checkout":
await markOrderPaid(event.requestId, event.txHash, deliveryId);
break;

case "payment.partial":
case "payment.partial.client_id":
case "payment.partial.checkout":
await recordPartialPayment(
event.requestId,
event.amount,
Expand Down Expand Up @@ -158,14 +150,14 @@
}
```

Webhook delivery is at least once, not exactly once. Each business operation must atomically record the delivery ID with the state it changes, then make a repeat delivery a successful no-op. If an operation calls another service, pass the delivery ID as that service's idempotency key too. A process can fail after a side effect but before it returns `200`.

Check warning on line 153 in use-cases/webhook-reconciliation.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/webhook-reconciliation.mdx#L153

Did you really mean 'idempotency'?

## Headers reference

| Header | Description |
| --- | --- |
| `x-request-network-signature` | HMAC-SHA256 of the raw JSON body, hex-encoded |
| `x-request-network-delivery` | ULID — use as idempotency key |

Check warning on line 160 in use-cases/webhook-reconciliation.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/webhook-reconciliation.mdx#L160

Did you really mean 'idempotency'?
| `x-request-network-retry-count` | `0`–`3`, current retry attempt |
| `x-request-network-test` | `true` only for `/v1/webhook/test` deliveries |

Expand All @@ -182,9 +174,9 @@

## Common patterns

### Idempotency

Check warning on line 177 in use-cases/webhook-reconciliation.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/webhook-reconciliation.mdx#L177

Did you really mean 'Idempotency'?

The same `payment.confirmed` event might arrive twice (network blip, retry overlap). Use `x-request-network-delivery` as the idempotency key. Record it atomically with the business update in your durable store; do not use a check-then-act cache lookup, because overlapping deliveries can both pass the check.

Check warning on line 179 in use-cases/webhook-reconciliation.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/webhook-reconciliation.mdx#L179

Did you really mean 'idempotency'?

For a local database update, add a `webhook_deliveries` table with a unique `delivery_id` column, then insert that ID in the same transaction as the business update:

Expand Down Expand Up @@ -233,7 +225,7 @@

### Crypto-to-fiat status tracking

The `payment.processing` event includes a `subStatus` field that progresses through `initiated → pending_internal_assessment → ongoing_checks → sending_fiat → fiat_sent`. Surface this in your UI so the payee sees real-time offramp progress.

Check warning on line 228 in use-cases/webhook-reconciliation.mdx

View check run for this annotation

Mintlify / Mintlify Validation (requestnetwork) - vale-spellcheck

use-cases/webhook-reconciliation.mdx#L228

Did you really mean 'offramp'?

## Local development

Expand Down