Skip to content
GitHub

Get an outgoing payment grant for future payments

Often, a transaction starts with the client getting the payment recipient’s details and asking the recipient’s ASE for permission to send money. However, Open Payments also supports a different approach: the client gets permission from the sender’s ASE to send money before knowing who the recipient will be. Web Monetization’s pay-as-you-browse model uses this approach.

Real-life use case: Web Monetization browser extension

The Web Monetization extension uses Open Payments to issue continuous outgoing payments to a web monetized site. The payments are issued on behalf of the user for as long as the user is on the site. The extension has no idea who the recipient will be until the user visits the site.

Setting up the extension requires the user to connect it to their wallet account. They specify a maximum amount they’re willing to spend and select whether the amount should automatically renew each month. The extension then receives an outgoing payment grant from the user’s wallet provider, allowing the extension to initiate future payments.

For this guide, you’ll assume the role of an app developer. The guide explains how to allow your app’s user to send payments of up to $100 CAD a month for three months without specifying a recipient beforehand.

Let’s assume your user saved their wallet address in their account profile when setting up your app. Call the GET Get Wallet Address API.

const userWalletAddress = await client.walletAddress.get({
url: 'https://cloudninebank.example.com/user'
})
Example response
{
"id": "https://cloudninebank.example.com/user",
"assetCode": "CAD",
"assetScale": 2,
"authServer": "https://auth.cloudninebank.example.com/",
"resourceServer": "https://cloudninebank.example.com/op"
}

2. Request an interactive outgoing payment grant

Section titled “2. Request an interactive outgoing payment grant”

Use the authorization server information received in the previous step to call the POST Grant Request API.

This call obtains a token that allows your app to request outgoing payment resources be created on your user’s wallet account.

Remember, your user wants to send payments of up to $100 CAD a month for three months. The amount resets each month and any unspent portions don’t roll over.

const grant = await client.grant.request(
{
url: userWalletAddress.authServer,
},
{
access_token: {
access: [
{
identifier: userWalletAddress.id,
type: 'outgoing-payment',
actions: ['read', 'create'],
limits: {
interval: 'R3/2025-05-20T13:00:00Z/P1M'
debitAmount: {
assetCode: 'CAD',
assetScale: 2,
value: '10000',
},
},
},
],
},
client: userWalletAddress.id,
interact: {
start: ['redirect'],
finish: {
method: 'redirect',
uri: 'https://paymentplatform.example/finish/{...}', // where to redirect the user to after they've completed the interaction
nonce: NONCE,
},
},
},
);
Example response
{
"interact": {
"redirect": "https://auth.interledger-test.dev/{...}", // uri to redirect the user to, to begin interaction
"finish": "..." // unique key to secure the callback
},
"continue": {
"access_token": {
"value": "..." // access token for continuing the outgoing payment grant request
},
"uri": "https://auth.interledger-test.dev/continue/{...}", // uri for continuing the outgoing payment grant request
"wait": 30
}
}

The interval used in this guide is R3/2025-05-20T13:00:00Z/P1M. Your user wants to send payments up to $100 CAD a month for three months. The interval breaks down like this:

  • R3/ is the number of repetitions - three.
  • 2025-05-20 is the start date of the repeating interval - 20 May 2025
  • T13:00:00Z/ is the start time of the repeating interval - 1:00 PM UTC.
  • P1M is the period between each interval - one month. Used with R3/, you have a grant that’s valid once a month for three months.

Altogether, your user can send up to $100 CAD from:

  • 1 PM UTC on 20 May 2025 through 12:59 PM UTC on 20 June 2025
  • 1 PM UTC on 20 June 2025 through 12:59 PM UTC on 20 July 2025
  • 1 PM UTC on 20 July 2025 through 12:59 PM UTC on 20 August 2025

Once the client receives the authorization server’s response, it must send the user to the interact.redirect URI contained in the response. This starts the interaction flow.

The response also includes a continue object, which is essential for managing the interaction and obtaining explicit user consent for outgoing payment grants. The continue object contains an access token and a URI that the client will use to finalize the grant request after the user has completed their interaction with the identity provider (IdP). This ensures that the client can securely obtain the necessary permissions to proceed with the payment process.

The user interacts with the authorization server through the server’s interface and approves or denies the grant.

Provided the user approves the grant, the authorization server:

  • Sends the user to the finish.uri provided in the interactive outgoing payment grant request. The means by which the server sends the user to the URI is out of scope, but common options include redirecting the user from a web page and launching the system browser with the target URI.
  • Secures the redirect by adding a unique hash, allowing your client to validate the finish call, and an interaction reference as query parameters to the URI.

In our example, we’re assuming the IdP the user interacted with has a user interface. When the interaction completes, your user is directed back to your app. Now the client can make a grant continuation request.

Call the POST Grant Continuation Request API. This call requests an access token that allows your app to request outgoing payment resources be created on the user’s wallet account.

Issue the request to the continue.uri provided in the initial outgoing payment grant response (Step 2).

Include the interact_ref returned in the redirect URI’s query parameters.

const userOutgoingPaymentGrant = await client.grant.continue(
{
accessToken: pendingUserOutgoingPaymentGrant.continue.acces_token.value,
url: pendingUserOutgoingPaymentGrant.continue.uri,
},
{
interact_ref: interactRef,
},
);

A successful response provides your app with an access token. Now your app can issue outgoing payment requests to future recipients against the grant. Each request must reference the access token.

Example response
{
"access_token": {
"value": "...", // final access token required before creating outgoing payments
"manage": "https://auth.cloudninebank.example.com/token/{...}", // management uri for access token
"access": [
{
"type": "outgoing-payment",
"actions": ["create", "read"],
"identifier": "https://cloudninebank.example.com/user",
}
]
},
"continue": {
"access_token": {
"value": "..." // access token for continuing the request
},
"uri": "https://auth.cloudninebank.example.com/continue/{...}" // continuation request uri
}
}