Skip to content
LogoLogo

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 eyJjaGFsbGVuZ2UiOnsiZXhwaXJlcyI6IjIwMjctMDEtMTVUMTI6MDU6MDBaIiwiaWQiOiJxQjN3RXJUeVU3aU9wQXNEOWZHaEprIiwiaW50ZW50IjoiY2hhcmdlIiwibWV0aG9kIjoidGVtcG8iLCJyZWFsbSI6Im1wcC5kZXYiLCJvcGFxdWUiOiJleUp5YjNWMFpTSTZJaTkyTVM5elpXRnlZMmdpZlEiLCJyZXF1ZXN0IjoiZXlKaGJXOTFiblFpT2lJeE1EQXdJaXdpWTNWeWNtVnVZM2tpT2lJd2VESXdZekF3TURBd01EQXdNREF3TURBd01EQXdNREF3TURBd01EQXdNREF3TURBd01EQXdNREFpTENKdFpYUm9iMlJFWlhSaGFXeHpJanA3SW1Ob1lXbHVTV1FpT2pReU1UZDlMQ0p5WldOcGNHbGxiblFpT2lJd2VHWXpPVVprTm1VMU1XRmhaRGc0UmpaR05HTmxObUZDT0RneU56STNPV05tWmtaaU9USXlOallpZlEifSwicGF5bG9hZCI6eyJoYXNoIjoiMHhhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiIiwidHlwZSI6Imhhc2gifSwic291cmNlIjoiZGlkOnBraDplaXAxNTU6NDIxNzoweDEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwYWJjZGVmMTIzNDU2NzgifQ

When 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 eyJjaGFsbGVuZ2UiOnsiZXhwaXJlcyI6IjIwMjctMDEtMTVUMTI6MDU6MDBaIiwiaGVhZGVyIjoiUGF5bWVudC1BdXRob3JpemF0aW9uIiwiaWQiOiJxQjN3RXJUeVU3aU9wQXNEOWZHaEprIiwiaW50ZW50IjoiY2hhcmdlIiwibWV0aG9kIjoidGVtcG8iLCJyZWFsbSI6Im1wcC5kZXYiLCJvcGFxdWUiOiJleUp5YjNWMFpTSTZJaTkyTVM5elpXRnlZMmdpZlEiLCJyZXF1ZXN0IjoiZXlKaGJXOTFiblFpT2lJeE1EQXdJaXdpWTNWeWNtVnVZM2tpT2lJd2VESXdZekF3TURBd01EQXdNREF3TURBd01EQXdNREF3TURBd01EQXdNREF3TURBd01EQXdNREFpTENKdFpYUm9iMlJFWlhSaGFXeHpJanA3SW1Ob1lXbHVTV1FpT2pReU1UZDlMQ0p5WldOcGNHbGxiblFpT2lJd2VHWXpPVVprTm1VMU1XRmhaRGc0UmpaR05HTmxObUZDT0RneU56STNPV05tWmtaaU9USXlOallpZlEifSwicGF5bG9hZCI6eyJoYXNoIjoiMHhhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiYWJhYmFiIiwidHlwZSI6Imhhc2gifSwic291cmNlIjoiZGlkOnBraDplaXAxNTU6NDIxNzoweDEyMzQ1Njc4OTBhYmNkZWYxMjM0NTY3ODkwYWJjZGVmMTIzNDU2NzgifQ

The 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

FieldDescription
challengeThe Challenge being responded to
sourceIdentity of the payer (address, DID, account ID)
payloadMethod-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:

  1. Verify the challenge.id matches an outstanding Challenge
  2. Verify the Challenge has not expired
  3. Verify the payment or proof using method-specific procedures
  4. 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.typeWhen the client uses itWhat the server verifies
transactionNon-zero charge in pull modeSigned Tempo transaction before broadcast
hashNon-zero charge in push modeOn-chain Receipt for the submitted transaction
proofZero-amount identity flowSigned 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.

Learn more