Skip to content
GitHub

Get an outgoing payment grant for future payments

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

In this model, a user installs a browser extension and visits a web monetized site. The extension uses Open Payments to issue continuous outgoing payments to the site, 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 renew monthly. The extension then receives an outgoing payment grant from the user’s wallet provider, allowing the extension to initiate future payments.

In this guide, you’ll assume the role of an app developer. The guide takes you through the steps your app will perform when requesting an outgoing payment grant without known recipients. The example scenario illustrates how to allow your app’s user to send payments of up to $100 CAD a month for three months.

Dependencies

Endpoints

Steps

1. Get user’s wallet address information

Let’s assume your user saved their wallet address in their account profile when setting up your app. Use this address to get the authorization server address, resource server address, and other details about your user’s wallet.

You should perform this step every time you request a grant in case something changes on your user’s side.

const walletAddress = await client.walletAddress.get({
url: 'https://cloudninebank.example.com/user'
})

2. Request an interactive outgoing payment grant

Your user indicates they want to make payments of up to $100 CAD a month for three months. The amount resets each month and any unspent portions don’t roll over.

Begin by requesting an interactive outgoing payment grant from the authorization server URL returned in the previous step.

Request requirements

The request must include:

  • debitAmount - The maximum amount that your user can send per interval. When the next interval begins, the value resets.
  • interval - The recurring time interval under which this grant is valid. Must be expressed in ISO 8601 format which is a standardized way to express time-related information.
  • interact object with a finish URI - The URI your user will be automatically sent to when interaction, described in the next steps, is complete.
const grant = await client.grant.request(
{
url: walletAddress.authServer,
},
{
access_token: {
access: [
{
identifier: walletAddress.id,
type: 'outgoing-payment',
actions: ['read', 'create'],
limits: {
interval: 'R3/2025-05-20T13:00:00Z/P1M'
debitAmount: {
assetCode: 'CAD',
assetScale: 2,
value: '10000',
},
},
},
],
},
client: clientWalletAddress.id,
interact: {
start: ['redirect'],
finish: {
method: 'redirect',
uri: 'https://cloudninebank.example.com/finish/T8jw5Xy',
nonce: NONCE,
},
},
},
);

About the interval

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

  • R[3]/ 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 of time between each interval - one month. Used with R[3], 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 UCT 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 UCT on 20 August 2025

3. Start interaction with the user

Once your 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.

Example response
{
"interact": {
"redirect": "https://auth.interledger-test.dev/4CF492MLVMSW9MKMXKHQ",
"finish": "4105340a-05eb-4290-8739-f9e2b463bfa7"
},
"continue": {
"access_token": {
"value": "33OMUKMKSKU80UPRY5NM"
},
"uri": "https://auth.interledger-test.dev/continue/4CF492MLVMSW9MKMXKHQ",
"wait": 30
}
}

4. Finish interaction with the user

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 your client’s previously defined finish.uri. 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.

5. Request a grant continuation

Use the interaction reference (interact_ref) returned in the redirect URI’s query parameters.

Continue grant

const grant = await client.grant.continue(
  {
    accessToken: CONTINUE_ACCESS_TOKEN,
    url: CONTINUE_URI,
  },
  {
    interact_ref: interactRef,
  },
);
Copied!

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.