Skip to content
Open
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
129 changes: 1 addition & 128 deletions use-cases/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -186,134 +186,7 @@ Save the `clientId` value (e.g. `cli_nz1bj41szV2fvjm9pbxdIhro3ld4x4`) — you'll

## Step 4: Register a Webhook

Webhooks let you receive real-time notifications when a payment is completed (or partially paid) for your payment links — no polling required.

Webhooks are scoped to the Client ID that creates them. Any payment link created with that Client ID will trigger the webhook. The webhook payload includes the `clientId` field so you can identify which Client ID the payment was associated with.

### Creating a webhook

Open [auth.request.network/open-api](https://auth.request.network/open-api/#tag/webhook/POST/v1/webhook) and call:

```http
POST https://auth.request.network/v1/webhook
x-client-id: <clientId-from-step-3>
Content-Type: application/json

{
"url": "https://mydomain.com/webhook"
}
```

**Example response (201 Created):**

```json
{
"id": "01KJC2WX8EH4MP3DHZB2YQ7N9G",
"secret": "f3c189a4b5e6d7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2"
}
```

<Warning>
The `secret` is only returned once at creation. Store it securely — you cannot retrieve it again. Use HTTPS in production. `localhost` URLs are accepted for local testing.
</Warning>

### Managing webhooks

Use the same Scalar docs page to list, toggle, or delete webhooks (all accept `x-client-id`):

| Method | Path | Purpose |
| --- | --- | --- |
| `GET` | `/v1/webhook` | List webhooks for this Client ID |
| `PUT` | `/v1/webhook/:webhookId` | Toggle active / inactive |
| `DELETE` | `/v1/webhook/:webhookId` | Permanently delete |
| `POST` | `/v1/webhook/test` | Body `{ "eventType": "payment.confirmed" }` — fire a test delivery |

### Verifying webhook signatures

Every webhook request includes a signature header so you can verify it came from Request Network:

| 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 |
| `x-request-network-retry-count` | Retry attempt number (`0`–`3`) |
| `x-request-network-test` | `true` only for test deliveries via `/v1/webhook/test` |

To verify, compute `HMAC-SHA256(rawBody, webhookSecret)` and compare it to `x-request-network-signature` using a constant-time comparison.

```typescript
import { createHmac, timingSafeEqual } from "node:crypto";

function verify(rawBody: string, signature: string | undefined, secret: string) {
if (!signature) return false;
const expected = createHmac("sha256", secret).update(rawBody).digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(signature, "hex");
return a.length === b.length && timingSafeEqual(a, b);
}
```

Always verify against the **raw** request body before parsing.

**Retries:** up to 3 retries (4 attempts total), default delays 1s / 5s / 15s, triggered on any non-2xx response, timeout, or connection error.

### 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).

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

```json
{
"event": "payment.confirmed",
"requestId": "01de2a889ee629c15b71b5d7964e3a7e87638c886be75bf1b9d2c1fbe64cf855fb",
"paymentReference": "0xabc123...",
"payee": "0x6923831ACf5c327260D7ac7C9DfF5b1c3cB3C7D7",
"amount": "10.0",
"totalAmountPaid": "10.0",
"expectedAmount": "10.0",
"timestamp": "2026-03-02T20:15:00.000Z",
"txHash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"network": "sepolia",
"currency": "FAU",
"paymentCurrency": "FAU",
"clientId": "cli_nz1bj41szV2fvjm9pbxdIhro3ld4x4",
"origin": "https://mydomain.com"
}
```

Key fields to look for:

| Field | Description |
| --- | --- |
| `event` | `payment.confirmed` (fully paid) or `payment.partial` (partial payment) |
| `requestId` | The request ID from when you created the payment link |
| `clientId` | The Client ID used to create the payment link — use this to route events if you have multiple Client IDs |
| `amount` | The amount paid in this transaction |
| `totalAmountPaid` | Cumulative amount paid so far |
| `expectedAmount` | The total amount expected |
| `txHash` | On-chain transaction hash |
| `network` | The blockchain network |
| `currency` | The token used for payment |

### All supported webhook events

| Event | Description |
| --- | --- |
| `payment.confirmed` | Payment fully confirmed |
| `payment.partial` | Partial payment received |
| `payment.failed` | Payment failed |
| `payment.refunded` | Payment refunded |
| `payment.processing` | Offramp processing started |
| `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 |

<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.
</Note>
Register a webhook for your Client ID to receive `payment.confirmed` when a payment completes. See the [Webhooks reference](/api-reference/webhooks) for endpoint setup, recipient routing, payloads, and signature verification.

## Step 5: Create a Secure Payment (Payment Link)

Expand Down