Skip to main content
This page is the single source an AI coding agent should read to integrate Request Network payments correctly. You’ll build a flow that accepts a crypto or stablecoin payment through a hosted payment link and confirms it via webhook. This flow uses the REST API directly — plain HTTP requests, with no extra dependency to install.

Which credentials do I actually need?

You need exactly two credentials, both obtained from the Dashboard or Auth API, both used server-side. You also need one non-secret value, the destinationId — see Where does the destinationId come from?.
string
required
The Client ID that identifies your application. Required on every request to create a payment link or register a webhook. Generate it in the Dashboard (Manage Destination → Client IDs → Generate New Client ID) or via POST /v1/client-ids on the Auth API.
string
required
The secret returned once when you register a webhook (POST /v1/webhook). Used to verify the HMAC-SHA256 signature on incoming webhook requests. It is shown exactly once at creation — store it securely (e.g. as an environment variable); it cannot be retrieved again.
For this integration the credential is the Client ID — do not ask the user for an API key. Request Network’s API does support an x-api-key header for server-to-server auth (see Authentication), but this flow does not need it, so never prompt the user for one.Never ask the user to paste a raw wallet address or a private key into your integration code either. Payment destinations are configured once in the Dashboard and referenced by their destinationId.The destinationId is a different thing from a credential: it is a non-secret identifier the user copies from their Dashboard, and you should ask the user for it (or have them create a payment destination) whenever your request body needs it. See Where does the destinationId come from?.

Where does the destinationId come from?

A payment destination encodes where money lands: wallet address, chain, and token, combined into one ERC-7828 composite ID of the form {humanReadableInteropAddress}:{tokenAddress}. You never construct or guess this value, and you never derive it from a raw wallet address. The user gets it from one of two places:
  • Dashboarddashboard.request.networkManage Destination. This is the same area where they generate the Client ID; in fact a payment destination must exist before the Client IDs section becomes available, so any user who already has a Client ID also has a destination to copy.
  • Auth APIGET /v1/payee-destination returns the active destination for the authenticated wallet (SIWE session), including its destinationId. Use this for automated provisioning. See Payee Destinations.
string
ERC-7828 composite destination ID for the payee. Optional only when the Client ID you authenticate with has a bound payee destination (payeeDestinationId) — the API then resolves the payee from the Client ID and you can omit the field. In every other case it is required.
The universally-safe path: always send destinationId, using a value the user copied from their Dashboard. Ask the user for it — say something like “paste the Destination ID from your Request Dashboard (Manage Destination)” — and if they don’t have a payment destination yet, tell them to create one first. Only omit the field when the user confirms their Client ID is bound to a destination. Never ship the sample value from the examples below; it is a placeholder that would send payments to someone else’s destination.

How should I call Request Network?

Call the REST API directly for server-side integrations. It needs no extra dependency, works from any language, and is the interface documented and versioned at api.request.network and auth.request.network. Make requests with fetch/curl/requests — there’s nothing to install.
1

Get a Client ID and a Destination ID

Obtain a clientId from the Dashboard (Manage Destination → Client IDs) or via POST /v1/client-ids on the Auth API. Store it as an environment variable, e.g. RN_CLIENT_ID.From the same Manage Destination area, have the user copy their destinationId and store it as e.g. RN_DESTINATION_ID. If the user has no payment destination yet, ask them to create one before continuing — the Client IDs section is only available once a destination exists. You can skip this value only if the user’s Client ID is bound to a payee destination.
2

Call the secure-payments endpoint

POST https://api.request.network/v2/secure-payments with the x-client-id header and a requests array containing at least one { destinationId, amount } item. Substitute the user’s own destinationId — the value in the examples below is a placeholder.
3

Share the returned URL

The response includes a securePaymentUrl. Redirect the payer there, or embed it as a link/button. The link expires after 7 days or once paid, whichever comes first.
Response (201 Created):
The destinationId in the request body is a composite value: {humanReadableInteropAddress}:{tokenAddress}. It comes from a payment destination already configured in the Dashboard — you do not construct it from a raw wallet address. Ask the user for their value rather than hardcoding one. Omit the field only when the user’s Client ID is bound to a payee destination.

How do I confirm a payment?

The webhook is the source of truth for payment confirmation — do not treat a successful POST /v2/secure-payments call or a payer’s browser redirect as proof of payment.
1

Register a webhook

POST https://auth.request.network/v1/webhook with x-client-id and a { "url": "https://yourdomain.com/webhook" } body. The response includes a one-time secret — store it as RN_WEBHOOK_SECRET.
2

Verify the signature on every incoming request

Compute HMAC-SHA256(rawBody, webhookSecret) and compare it to the x-request-network-signature header using a constant-time comparison. You must use the raw request body — verifying against a re-serialized (e.g. JSON.stringify(req.body)) body will produce a mismatch and silently break verification.
3

Handle payment.confirmed / payment.partial

A fully paid request fires payment.confirmed; a partial payment fires payment.partial. Match the requestId in the payload to the requestIds you received when creating the payment link. Fulfill the order only after payment.confirmedpayment.partial is an intermediate state, not a completed payment.
Always verify against the raw request body, before any JSON parsing. Frameworks like Express parse and re-serialize the body by default (express.json()); if you re-stringify req.body instead of using the original raw bytes, the computed HMAC will not match and every verification will fail (or worse, be skipped entirely if the check is disabled to “fix” it).
Fallback (optional polling): GET https://api.request.network/v2/request/{requestId} returns { hasBeenPaid: boolean, ... }. Send the x-client-id header — the call is unauthenticated without it:
Use this only as a fallback if a webhook delivery is missed — it is not a replacement for webhook handling.
This call runs from your server, so it needs a backend Client ID — one created with no Allowed Domains. A Client ID that has Allowed Domains set is validated against the Origin header, and a server-side request sends no Origin, so it will be rejected. See Client ID Management.

How do I handle cross-chain payments?

When a payer pays from a different chain than your payment destination, settlement is not instantaneous. Your thank-you or confirmation page must treat the payment as pending until the webhook confirms it — never mark a payment as complete purely because the payer’s browser reported a successful transaction submission or redirected back to your site. Drive the final “paid” state exclusively from the payment.confirmed webhook (or the hasBeenPaid polling fallback), not from front-end submission success.

Common mistakes

  • Asking the user for an API key, a private key, or a raw wallet address. For this flow you need only the Client ID, the webhook secret, and the user’s Destination ID.
  • Hardcoding the sample destinationId from the docs instead of asking the user for theirs — payments would go to the sample destination, not to the user.
  • Verifying the webhook signature against a re-serialized body (e.g. JSON.stringify(req.body)) instead of the raw request body.
  • Assuming a cross-chain payment is final as soon as the payer’s browser confirms submission, instead of waiting for the payment.confirmed webhook.
  • Adding an unnecessary dependency instead of calling the documented REST API directly.
Last modified on July 30, 2026