Credentials
Client-submitted payment proofs
A Credential is your response to a Challenge, proving that you paid or authorized the payment. Send Credentials in Authorization unless the Challenge advertises another field in its header parameter.
Structure
Authorization: Payment eyJjaGFsbGVuZ2UiOnsiZXhwaXJlcyI6IjIwMjctMDEtMTVUMTI6MDU6MDBaIiwiaWQiOiJxQjN3RXJUeVU3aU9wQXNEOWZHaEprIiwiaW50ZW50IjoiY2hhcmdlIiwibWV0aG9kIjoidGVtcG8iLCJyZWFsbSI6Im1wcC5kZXYiLCJvcGFxdWUiOiJleUp5YjNWMFpTSTZJaTkyTVM5elpXRnlZMmdpZlEiLCJyZXF1ZXN0IjoiZXlKaGJXOTFiblFpT2lJeE1EQXdJaXdpWTNWeWNtVnVZM2tpT2lJd2VESXdZekF3TURBd01EQXdNREF3TURBd01EQXdNREF3TURBd01EQXdNREF3TURBd01EQXdNREFpTENKdFpYUm9iMlJFWlhSaGFXeHpJanA3SW1Ob1lXbHVTV1FpT2pReU1UZDlMQ0p5WldOcGNHbGxiblFpT2lJd2VHWXpPVVprTm1VMU1XRmhaRGc0UmpaR05HTmxObUZDT0RneU56STNPV05tWmtaaU9USXlOallpZlEifSwicGF5bG9hZCI6eyJoYXNoIjoiMHhhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiIiwidHlwZSI6Imhhc2gifSwic291cmNlIjoiZGlkOnBraDplaXAxNTU6NDIxNzoweDEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwYWJjZGVmMTIzNDU2NzgifQWhen an endpoint also uses Authorization for application authentication, the server can advertise header="Payment-Authorization". Keep the ordinary Credential and Payment Credential separate:
Authorization: Bearer application-token
Payment-Authorization: Payment eyJjaGFsbGVuZ2UiOnsiZXhwaXJlcyI6IjIwMjctMDEtMTVUMTI6MDU6MDBaIiwiaGVhZGVyIjoiUGF5bWVudC1BdXRob3JpemF0aW9uIiwiaWQiOiJxQjN3RXJUeVU3aU9wQXNEOWZHaEprIiwiaW50ZW50IjoiY2hhcmdlIiwibWV0aG9kIjoidGVtcG8iLCJyZWFsbSI6Im1wcC5kZXYiLCJvcGFxdWUiOiJleUp5YjNWMFpTSTZJaTkyTVM5elpXRnlZMmdpZlEiLCJyZXF1ZXN0IjoiZXlKaGJXOTFiblFpT2lJeE1EQXdJaXdpWTNWeWNtVnVZM2tpT2lJd2VESXdZekF3TURBd01EQXdNREF3TURBd01EQXdNREF3TURBd01EQXdNREF3TURBd01EQXdNREFpTENKdFpYUm9iMlJFWlhSaGFXeHpJanA3SW1Ob1lXbHVTV1FpT2pReU1UZDlMQ0p5WldOcGNHbGxiblFpT2lJd2VHWXpPVVprTm1VMU1XRmhaRGc0UmpaR05HTmxObUZDT0RneU56STNPV05tWmtaaU9USXlOallpZlEifSwicGF5bG9hZCI6eyJoYXNoIjoiMHhhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiIiwidHlwZSI6Imhhc2gifSwic291cmNlIjoiZGlkOnBraDplaXAxNTU6NDIxNzoweDEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwYWJjZGVmMTIzNDU2NzgifQThe custom-header Credential above decodes to this JSON object. It echoes the Challenge example unchanged and uses an illustrative transaction hash:
{
"challenge": {
"expires": "2027-01-15T12:05:00Z",
"header": "Payment-Authorization",
"id": "qB3wErTyU7iOpAsD9fGhJk",
"intent": "charge",
"method": "tempo",
"opaque": "eyJyb3V0ZSI6Ii92MS9zZWFyY2gifQ",
"realm": "mpp.dev",
"request": "eyJhbW91bnQiOiIxMDAwIiwiY3VycmVuY3kiOiIweDIwYzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJtZXRob2REZXRhaWxzIjp7ImNoYWluSWQiOjQyMTd9LCJyZWNpcGllbnQiOiIweGYzOUZkNmU1MWFhZDg4RjZGNGNlNmFCODgyNzI3OWNmZkZiOTIyNjYifQ"
},
"payload": {
"hash": "0xabababababababababababababababababababababababababababababababab",
"type": "hash"
},
"source": "did:pkh:eip155:4217:0x1234567890abcdef1234567890abcdef12345678"
}The echoed Challenge keeps the original HTTP wire values. In a Credential, challenge.request and challenge.opaque remain the same base64url-encoded JCS JSON strings from the WWW-Authenticate header.
Fields
| Field | Description |
|---|---|
challenge | The Challenge being responded to |
source | Identity of the payer (address, DID, account ID) |
payload | Method-specific payment proof |
Enforce single-use Credentials
Payment proofs must be accepted at most once. Challenge binding prevents clients from modifying payment terms, but it doesn't detect reuse—each payment method must enforce replay protection separately. When processing a Credential:
- Verify the
challenge.idmatches an outstanding Challenge - Verify the Challenge has not expired
- Verify the payment or proof using method-specific procedures
- Reject any replayed Credentials
For Tempo zero-dollar proof Credentials, pass a shared atomic store to tempo.charge. Without a store, a valid proof remains reusable until its Challenge expires.
Tempo charge payload types
Tempo charge currently uses three Credential payload shapes:
payload.type | When the client uses it | What the server verifies |
|---|---|---|
transaction | Non-zero charge in pull mode | Signed Tempo transaction before broadcast |
hash | Non-zero charge in push mode | On-chain Receipt for the submitted transaction |
proof | Zero-amount identity flow | Signed proof message over the Challenge ID with no on-chain transfer |
Example
Tempo charge payment
{
"challenge": {
"expires": "2027-01-15T12:05:00Z",
"header": "Payment-Authorization",
"id": "qB3wErTyU7iOpAsD9fGhJk",
"intent": "charge",
"method": "tempo",
"opaque": "eyJyb3V0ZSI6Ii92MS9zZWFyY2gifQ",
"realm": "mpp.dev",
"request": "eyJhbW91bnQiOiIxMDAwIiwiY3VycmVuY3kiOiIweDIwYzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJtZXRob2REZXRhaWxzIjp7ImNoYWluSWQiOjQyMTd9LCJyZWNpcGllbnQiOiIweGYzOUZkNmU1MWFhZDg4RjZGNGNlNmFCODgyNzI3OWNmZkZiOTIyNjYifQ"
},
"payload": {
"hash": "0xabababababababababababababababababababababababababababababababab",
"type": "hash"
},
"source": "did:pkh:eip155:4217:0x1234567890abcdef1234567890abcdef12345678"
}The server looks up the transaction hash and verifies that the confirmed transfer matches the Challenge parameters.
For zero-amount Tempo Challenges, the payload becomes {"type":"proof","signature":"0x..."}. The server verifies the proof against the source identity instead of broadcasting a transfer.