Open Payments and SPSP

As discussed on the call today we’re thinking about how to relate Payment Pointers, SPSP and Open Payments in a simple way that is easy to understand and work with.

This is the proposal, please share comments and suggestions below.


SPSP defines the concept of an SPSP endpoint which locates a JSON document resource providing, at a minimum, STREAM credentials to use to open a new STREAM connection.

The expected behaviour of an SPSP client (which queries the endpoint) is to establish the STREAM connection and begin sending money to the entity that provided the SPSP endpoint URL.

SPSP only defines the expected behaviour of the service (an SPSP server) hosting the SPSP endpoint when queried via an HTTP GET request.

The use of SPSP is indicated by the SPSP client through the Accept header which contains a value of application/spsp4+json.

Therefore SPSP was designed to accommodate future versions that expand upon the functionality in the current version. It is possible for a future version of the protocol to extend the schema of the JSON document and define new behaviours for other HTTP verbs and still remain backwards compatible with SPSP.

Open Payments

We propose that Open Payments is the next version of SPSP.

It defines a super-set of behaviours of the systems that were previously called the SPSP client and SPSP server. All operations center around the Open Payments endpoint (the URL resolved from the user’s Payment Pointer)

Open Payments clients SHOULD still be able interact with a legacy SPSP server to send money via monetization (as currently supported by SPSP).

Open Payments expands the set of media types defined to include the following:

  • Mandate: application/mandate+json
  • Invoice: application/invoice+json
  • Monetization: application/monetization+json

For backwards compatibility, clients and servers SHOULD accept both application/monetization+json and application/spsp4+json media types and treat these as equivalent.


In Open Payments, a GET request against the Open Payments endpoint returns either a monetization resource or the metadata for the account. The response differs depending on the content-type requested.

When performing a GET request against an Open Payments endpoint for monetization, Open Payments clients SHOULD send two Accept headers indicating support for Open Payments but also for SPSP (but indicating a preference for Open Payments via the order of the headers).

Example request for Payment Pointer $wallet.example/adrian :

GET /adrian HTTP/1.1
Host: wallet.example
Accept: application/monetization+json, application/spsp4+json

Example response

HTTP/1.1 200 OK
Content-Type: application/monetization+json

  "ilpAddress": "",
  "sharedSecret": "6jR5iNIVRvqeasJeCty6C+YB5X9FhSOUPCL/5nha5Vs="

Example request 2:

GET /adrian HTTP/1.1
Host: wallet.example
Accept: application/json

Example response

HTTP/1.1 200 OK
Content-Type: application/json

  "issuer": "https://wallet.example",
  "assets_supported": [
    {"code": "USD", "scale": 2},
    {"code": "EUR", "scale": 2}

To create invoices and mandates the client does a POST request against the Open Payments endpoint and indicates the type of resource being created through the media type:

Verb Content-Type or Accept header Action
GET application/monetization+json Return STREAM credentials
GET application/json Return account metadata
POST application/invoice+json Create an invoice resource
POST application/mandate+json Create a mandate resource

Legacy SPSP servers will return an empty response or an error making it simple for clients to detect that the receiver only supports SPSP and not Open Payments and falling back to using Monetization if possible to complete the payment.


After the call, I do wonder if we should change from Monetization to Payment?

GET /adrian HTTP/1.1
Host: wallet.example
Accept: application/payment+json, application/spsp4+json


HTTP/1.1 200 OK
Content-Type: application/payment+json

  "ilpAddress": "",
  "sharedSecret": "6jR5iNIVRvqeasJeCty6C+YB5X9FhSOUPCL/5nha5Vs="

Payment doesn’t encapsulate the transient nature of what that endpoint represents. It also fits nicely with "Web"monetization and potentially other monetization things like Codius.


1 Like

I think we should consider adding some sort of OpenPayments indicator into our mime types. One thing I’ve noticed is that terms like Invoice at least are not unique to OpenPayments.

What if we did something like this for namespacing:

Verb Content-Type or Accept header Action
GET application/openpayments-monetization+json Return STREAM credentials
GET application/openpayments+json Return account metadata (option1)
GET application/json Return account metadata (option2)
POST application/openpayments-invoice+json Create an invoice resource
POST application/openpayments-mandate+json Create a mandate resource

Or I suppose this:

Verb Content-Type or Accept header Action
GET application/op-monetization+json Return STREAM credentials
GET application/op+json Return account metadata (option1)
GET application/json Return account metadata (option2)
POST application/op-invoice+json Create an invoice resource
POST application/op-mandate+json Create a mandate resource

Is that already included in Open Payments or is that also new?

I think I prefer “monetization”. “payment” seems too broad of a concept.

That is the server meta data currently made available via a more complex discovery flow.

This document would also contain meta-data but would be specific to this user/account holder so could hold user specific data (like a public key or cert issued by the wallet…)

I agree with Matt’s comments – plus, application/payment+json is much too broad. What we’re really meaning by this MIME type is: “Get STREAM credentials” no?

It seems like this MIME type should be something related to what it’s doing, like application/ilp-stream+json or application/stream-setup+json or something like that.

For example, what if I want to get STREAM credentials but I don’t want to do Web Monetization?

I also considered application/stream-connection+json?

My issue with “monetization” is it’s a pretty abstract term (especially compared to mandate and invoice).

1 Like

I like ‘application/stream-connection+json’

I agree monetization is abstract but we can define what the concrete version is of it. I have an issue with application/stream-connection+json because all payments are made with a STREAM connection. There is nothing unique about it. Hence just asking for a random stream connection details doesn’t mean anything IMO.

Also by using stream for that payment type could create confusion for new people as we overload the term

1 Like

Why not


But neither does asking for an invoice or mandate without context?

If we think about what a PP represents and what we’re doing I think this fits well with the REST paradigm, even if it’s not “pure” REST.

A PP represents a user with an account.

If I do a GET on that PP resource URL I get back details of the user and account such as the URL of th auth endpoint to get authorization to access the account, the URL of the userinfo endpoint to get an OpenID Connect compliant ID token, maybe a public key that can be used to authenticate the user?

If I do a GET on that PP resource but want credentials to send money to the account then I ask for the application/stream-credentials+json media type and I get back STREAM credentials.

A use case for this is web monetization but there may be others such as in a Codius ecosystem

My initial impression is I really like that the discovery is simplified, and like that this will reduce latency. I’m not entirely sold on differentiating the type of request based on content types, but don’t have strong opinions on that :+1:

I think “invoice” is sufficiently descriptive since it indicates the recipient is defining the amount due, and the “openpayments” prefix provides the context that the (e.g.) “mandate” is for a payment.

Whereas, I don’t think stream-credentials differentiates between the class of payment since, as Matt noted, they’re all executed using STREAM.

I think about, what is SPSP/this mode used for? To setup payments that (1) have no fixed destination amount, and (2) are initiated by the payer – right? My understanding is the primary use case for this is payments with no direct quid pro quo, such as tips or donations: I want you to get as much money as possible, but don’t care how much. The recipient instituted no condition on the payment.

While this is how Web Monetization uses SPSP, I’m not sure “monetization” is a great descriptor for that type of payment, since the payee could earn revenue (monetize) via any of these classes of payments.

Spitballing, maybe something like application/openpayments-unconditional+json?

1 Like

I think it’s the best we have that HTTP already offers. By using headers we get a lot of support in things like ingress for differential routing so I think it must be either header or path based.

We COULD use a URL param but that seems odd in a POST… or maybe not?
i.e. PP = $wallet.example/alice, URL to POST to for new invoice = https://wallet.example/alice?resource=invoice

I agree with your logic but I don’t agree that the media type should indicate the purpose, it indicates the type. i.e. If you do a GET and you want to get back STREAM credentials then ask for that type (irrespective of what you will use them for).

i also have some problem on open payments & spsp, anybody here who will explain this to me