<!--
Sitemap:
- [MPP — Machine Payments Protocol](/index): MPP (Machine Payments Protocol) is the open standard for machine-to-machine payments—co-developed by Tempo and Stripe. Charge for API requests, tool calls, and content via HTTP 402.
- [Page Not Found](/404)
- [Brand assets and guidelines](/brand): Download official MPP logos, wordmarks, and brand assets. Guidelines for using the Machine Payments Protocol brand in your project or integration.
- [Extensions](/extensions): Community-built tools and integrations for MPP
- [Frequently asked questions](/faq): Answers to common questions about MPP—payment methods, settlement, pricing, security, and how the protocol compares to API keys and subscriptions.
- [Machine Payments Protocol](/overview): MPP standardizes HTTP 402 for machine-to-machine payments. Learn how agents, apps, and services exchange payments in the same HTTP request.
- [Payment methods](/payment-methods/): Available methods and how to choose one
- [Protocol overview](/protocol/): The Machine Payments Protocol standardizes HTTP 402 with an extensible challenge–credential–receipt flow that works with any payment network.
- [Quickstart](/quickstart/): Get started with MPP in minutes. Protect your API with payments, connect your agent, or integrate your app with MPP-enabled services.
- [SDKs and client libraries](/sdk/): Official MPP SDKs in TypeScript, Python, and Rust, plus community SDKs in other languages.
- [Discovery](/advanced/discovery): Advertise your API's payment terms with an OpenAPI discovery document so clients and agents know what endpoints cost before making requests.
- [Identity](/advanced/identity): Use MPP Credentials for access control, rate limiting, and multi-step workflows—without requiring payment.
- [Refunds](/advanced/refunds): Return funds to clients after a charge, or let sessions refund unused deposits automatically.
- [Build with an LLM](/guides/building-with-an-llm): Use llms-full.txt to give your agent complete MPP context.
- [Accept multiple payment methods](/guides/multiple-payment-methods): Accept Tempo stablecoins, Stripe cards, and Lightning Bitcoin on a single API endpoint. Serve a multi-method 402 Challenge and let clients choose.
- [Accept one-time payments](/guides/one-time-payments): Charge per request with a payment-gated API
- [Accept pay-as-you-go payments](/guides/pay-as-you-go): Build a payment-gated API with session-based billing using mppx payment channels. Charge per request with near-zero latency overhead.
- [Proxy an existing service](/guides/proxy-existing-service): Put a payment gate in front of any API without changing its code. Use the mppx Proxy SDK to charge for upstream access.
- [Accept split payments](/guides/split-payments): Distribute a charge across multiple recipients
- [Accept streamed payments](/guides/streamed-payments): Accept streamed payments over Server-Sent Events with mppx. Bill per token in real time using Tempo payment channels for LLM inference APIs.
- [Charge intent for one-time payments](/intents/charge): Immediate one-time payments
- [Card payment method](/payment-methods/card/): Card payments via encrypted network tokens
- [Custom payment methods](/payment-methods/custom): Build your own payment method
- [Lightning](/payment-methods/lightning/): Bitcoin payments over the Lightning Network
- [Solana](/payment-methods/solana/): Native SOL and SPL token payments
- [Stellar SEP-41 token payments](/payment-methods/stellar/): SEP-41 token payments on the Stellar network
- [Stripe payment method](/payment-methods/stripe/): Cards, wallets, and other Stripe supported payment methods
- [Tempo stablecoin payments](/payment-methods/tempo/): Stablecoin payments on the Tempo blockchain
- [Challenges](/protocol/challenges): Server-issued payment requirements
- [Credentials](/protocol/credentials): Client-submitted payment proofs
- [HTTP 402 payment required](/protocol/http-402): HTTP 402 Payment Required signals that a resource requires payment. Learn when and how MPP servers return 402 with a WWW-Authenticate Challenge.
- [Payment receipts and verification](/protocol/receipts): Receipts confirm successful payment in MPP. Return them in the Payment-Receipt header so clients can verify that the server accepted their Credential.
- [Transports](/protocol/transports/): MPP defines transport bindings for HTTP and MCP. Learn how Challenges, Credentials, and Receipts map to headers and JSON-RPC messages.
- [Use with agents](/quickstart/agent): Connect your coding agent to MPP-enabled services. Set up Tempo Wallet or the mppx SDK to handle 402 payment flows automatically.
- [Use with your app](/quickstart/client): Handle payment-gated resources in your app. Use the mppx client SDK to intercept 402 responses, pay, and retry—all automatically.
- [Add payments to your API](/quickstart/server): Add payment-gated access to your API with mppx. Accept stablecoins, cards, and Bitcoin in a few lines of code using the MPP server SDK.
- [SDK features](/sdk/features): Feature parity across TypeScript, Python, and Rust MPP SDKs.
- [Python SDK](/sdk/python/): The pympp Python library
- [Rust SDK for MPP](/sdk/rust/): The mpp Rust library
- [Getting started](/sdk/typescript/): The mppx TypeScript library
- [Card charge](/payment-methods/card/charge): One-time payments using encrypted network tokens
- [Lightning charge](/payment-methods/lightning/charge): One-time payments using BOLT11 invoices
- [Lightning session](/payment-methods/lightning/session): Pay-as-you-go payments over Lightning
- [Solana charge](/payment-methods/solana/charge): One-time payments on Solana
- [Stellar charge](/payment-methods/stellar/charge): One-time SEP-41 token transfers
- [Channel](/payment-methods/stellar/session): High-frequency off-chain payments
- [Stripe charge](/payment-methods/stripe/charge): One-time payments using Shared Payment Tokens
- [Tempo charge](/payment-methods/tempo/charge): One-time TIP-20 token transfers
- [Session](/payment-methods/tempo/session): Low-cost high-throughput payments
- [HTTP transport](/protocol/transports/http): The HTTP transport maps MPP payment flows to standard HTTP headers—WWW-Authenticate for Challenges, Authorization for Credentials, and Payment-Receipt.
- [MCP and JSON-RPC transport](/protocol/transports/mcp): Payment flows for AI tool calls
- [Python MPP client](/sdk/python/client): Handle 402 responses automatically
- [Core Types](/sdk/python/core): Challenge, Credential, and Receipt primitives
- [Server](/sdk/python/server): Protect endpoints with payment requirements
- [Client](/sdk/rust/client): Handle 402 responses automatically
- [Core types](/sdk/rust/core): Challenge, Credential, and Receipt primitives
- [Server](/sdk/rust/server): Protect endpoints with payment requirements
- [CLI Reference](/sdk/typescript/cli): Built-in command-line tool for paid HTTP requests
- [Method.from](/sdk/typescript/Method.from): Create a payment method from a definition
- [Paid API proxy server](/sdk/typescript/proxy): Paid API proxy
- [McpClient.wrap](/sdk/typescript/client/McpClient.wrap): Payment-aware MCP client
- [stripe client method](/sdk/typescript/client/Method.stripe): Register all Stripe intents
- [Method.stripe.charge](/sdk/typescript/client/Method.stripe.charge): One-time payments via Shared Payment Tokens
- [tempo client method](/sdk/typescript/client/Method.tempo): Register all Tempo intents
- [Method.tempo.charge](/sdk/typescript/client/Method.tempo.charge): One-time payments
- [Method.tempo.session](/sdk/typescript/client/Method.tempo.session): Low-cost high-throughput payments
- [tempo.session](/sdk/typescript/client/Method.tempo.session-manager): Standalone session manager
- [Mppx.create](/sdk/typescript/client/Mppx.create): Create a payment-aware fetch client
- [Mppx.restore](/sdk/typescript/client/Mppx.restore): Restore the original global fetch
- [Transport.from](/sdk/typescript/client/Transport.from): Create a custom transport
- [Transport.http](/sdk/typescript/client/Transport.http): HTTP transport for payments
- [Transport.mcp](/sdk/typescript/client/Transport.mcp): MCP transport for payments
- [BodyDigest.compute](/sdk/typescript/core/BodyDigest.compute): Compute a body digest hash
- [BodyDigest.verify](/sdk/typescript/core/BodyDigest.verify): Verify a body digest hash
- [Challenge.deserialize](/sdk/typescript/core/Challenge.deserialize): Deserialize a Challenge from a header
- [Challenge.from](/sdk/typescript/core/Challenge.from): Create a new Challenge
- [Challenge.fromHeaders](/sdk/typescript/core/Challenge.fromHeaders): Extract a Challenge from Headers
- [Challenge.fromMethod](/sdk/typescript/core/Challenge.fromMethod): Create a Challenge from a method
- [Challenge.fromResponse](/sdk/typescript/core/Challenge.fromResponse): Extract a Challenge from a Response
- [Challenge.meta](/sdk/typescript/core/Challenge.meta): Extract correlation data from a Challenge
- [Challenge.serialize](/sdk/typescript/core/Challenge.serialize): Serialize a Challenge to a header
- [Challenge.verify](/sdk/typescript/core/Challenge.verify): Verify a Challenge HMAC
- [Credential.deserialize](/sdk/typescript/core/Credential.deserialize): Deserialize a Credential from a header
- [Credential.from](/sdk/typescript/core/Credential.from): Create a new Credential
- [Credential.fromRequest](/sdk/typescript/core/Credential.fromRequest): Extract a Credential from a Request
- [Credential.serialize](/sdk/typescript/core/Credential.serialize): Serialize a Credential to a header
- [Expires utility functions](/sdk/typescript/core/Expires): Generate relative expiration timestamps
- [Method.from](/sdk/typescript/core/Method.from): Create a payment method definition
- [Method.toClient](/sdk/typescript/core/Method.toClient): Extend a method with client logic
- [Method.toServer](/sdk/typescript/core/Method.toServer): Extend a method with server verification
- [PaymentRequest.deserialize](/sdk/typescript/core/PaymentRequest.deserialize): Deserialize a payment request
- [PaymentRequest.from](/sdk/typescript/core/PaymentRequest.from): Create a payment request
- [PaymentRequest.serialize](/sdk/typescript/core/PaymentRequest.serialize): Serialize a payment request to a string
- [Receipt.deserialize](/sdk/typescript/core/Receipt.deserialize): Deserialize a Receipt from a header
- [Receipt.from](/sdk/typescript/core/Receipt.from): Create a new Receipt
- [Receipt.fromResponse](/sdk/typescript/core/Receipt.fromResponse): Extract a Receipt from a Response
- [Receipt.serialize](/sdk/typescript/core/Receipt.serialize): Serialize a Receipt to a string
- [Elysia payment middleware](/sdk/typescript/middlewares/elysia): Payment middleware for Elysia
- [Express payment middleware](/sdk/typescript/middlewares/express): Payment middleware for Express
- [Hono payment middleware](/sdk/typescript/middlewares/hono): Payment middleware for Hono
- [Next.js payment middleware](/sdk/typescript/middlewares/nextjs): Payment middleware for Next.js
- [stripe](/sdk/typescript/server/Method.stripe): Register all Stripe intents
- [Method.stripe.charge](/sdk/typescript/server/Method.stripe.charge): One-time payments via Shared Payment Tokens
- [tempo server method](/sdk/typescript/server/Method.tempo): Register all Tempo intents
- [Method.tempo.charge](/sdk/typescript/server/Method.tempo.charge): One-time stablecoin payments
- [Method.tempo.session](/sdk/typescript/server/Method.tempo.session): Low-cost high-throughput payments
- [Mppx.compose](/sdk/typescript/server/Mppx.compose): Present multiple payment options
- [Mppx.create](/sdk/typescript/server/Mppx.create): Create a server-side payment handler
- [Mppx.toNodeListener](/sdk/typescript/server/Mppx.toNodeListener): Adapt payments for Node.js HTTP
- [Request.toNodeListener](/sdk/typescript/server/Request.toNodeListener): Convert Fetch handlers to Node.js
- [Response.requirePayment](/sdk/typescript/server/Response.requirePayment): Create a 402 response
- [Transport.from](/sdk/typescript/server/Transport.from): Create a custom transport
- [Transport.http](/sdk/typescript/server/Transport.http): HTTP server-side transport
- [Transport.mcp](/sdk/typescript/server/Transport.mcp): Raw JSON-RPC MCP transport
- [Transport.mcpSdk](/sdk/typescript/server/Transport.mcpSdk): MCP SDK server-side transport
-->

# Identity \[Verify clients without payment]

Every MPP Credential carries a cryptographic identity—the client's public key in the `source` field. Your server gets a verified identity on every request, whether the client pays or not. This page covers how to use that identity for access control, gating, and multi-step workflows.

## Overview

The Credential already proves the client controls a specific public key. The `source` field is the same regardless of the payment amount, which means you can use the MPP flow purely for identity when payment isn't needed.

Extract the client's identity from any verified request using `Credential.fromRequest`:

```ts twoslash [server.ts]
// @noEmit
declare const request: Request
// ---cut---
import { Credential } from 'mppx'

const credential = Credential.fromRequest(request)
const clientIdentity = credential.source
// @log: "did:pkh:eip155:4217:0x1234..."
```

Backends key workloads, sessions, and access control on this public key. The payment flow is orthogonal to identity, and can be used in combination with other methods.

### Methods

| Method | Payment methods | Description |
|--------|-----------------|-------------|
| [**Zero-dollar auth**](#zero-dollar-auth) | Tempo, Lightning, Solana, Custom | Proves key ownership with no funds transfer |

:::info\[More methods coming]
Additional identity methods are in development. Check back for updates.
:::

## Zero-dollar auth

Zero-dollar auth uses the standard Challenge → Credential flow with the amount set to `0`. The client signs the Challenge to prove key ownership. No funds move on-chain, and no additional protocol extensions are required.

For Tempo charge, zero-dollar auth now uses a `proof` Credential payload instead of a real transaction. The client signs a proof message over the Challenge ID, and the server verifies that signature against the `source` DID.

:::warning\[Replay protection]
By default, a valid zero-dollar proof remains reusable until the Challenge expires. Pass `store` to `tempo.charge()` when you want single-use proof auth. In a multi-instance deployment, use a shared store so every instance sees consumed proofs.
:::

<MermaidDiagram
  chart={`sequenceDiagram
  participant Client
  participant Server
  Client->>Server: (1) GET /resource
  Server-->>Client: (2) 402 + Challenge (amount: 0)
  Note over Client: (3) Sign proof message
  Client->>Server: (4) GET /resource + Credential
  Note over Server: (5) Verify proof
  Server-->>Client: (6) 200 OK
`}
/>

The Credential contains the client's public key and a valid signature, giving the server a verified identity to associate with the request.

For Tempo, the server rejects `transaction` and `hash` payloads for zero-amount Challenges and requires `proof`.

### Case study: long-running jobs

A service accepts a paid request to start work, then lets the client poll for results using zero-dollar auth. The server keys workloads on the client's public key.

::::steps

#### Client submits a job (paid)

The client sends a request with payment to create a new job. The Credential includes both payment proof and the client's public key.

```ts twoslash [client.ts]
// @noEmit
declare const fetch: (url: string) => Promise<{ json(): Promise<any> }>
// ---cut---
const response = await fetch('https://api.example.com/v1/jobs')
const { jobId } = await response.json()
// @log: { jobId: "abc123" }
```

The server records the job and associates it with the client's public key from the Credential.

#### Server stores the public key

After the payment middleware verifies the Credential, extract the client's identity from the request:

```ts [server.ts]
import { Credential } from 'mppx'

export async function handler(request: Request) {
  const result = await mppx.charge({ amount: '1.00' })(request)
  if (result.status === 402) return result.challenge

  const credential = Credential.fromRequest(request)
  const pubkey = credential.source // [!code hl]
  const jobId = createJob({ owner: pubkey }) // [!code hl]

  return result.withReceipt(Response.json({ jobId }))
}
```

#### Client polls for status (zero-dollar auth)

The client polls the job endpoint. The server issues a zero-dollar Challenge—the client signs it to prove they own the same key.

```ts [server.ts]
export async function statusHandler(request: Request) {
  const result = await mppx.charge({ amount: '0' })(request) // [!code hl]
  if (result.status === 402) return result.challenge

  const credential = Credential.fromRequest(request)
  const job = getJob(jobIdFromUrl(request))

  if (job.owner !== credential.source) {
    return Response.json({ error: 'Not your job' }, { status: 403 })
  }

  return result.withReceipt(Response.json({ result: job.result, status: job.status }))
}
```

::::

### Case study: paid unlock with free access

A service charges once to unlock a resource, then grants repeated free access tied to the client's identity. This replaces API keys with cryptographic ownership.

::::steps

#### Client pays to unlock

The client pays once to gain access. The server records the public key as an authorized user.

```ts [server.ts]
export async function unlockHandler(request: Request) {
  const result = await mppx.charge({ amount: '50.00' })(request)
  if (result.status === 402) return result.challenge

  const credential = Credential.fromRequest(request)
  grantAccess({ dataset: 'premium', owner: credential.source }) // [!code hl]

  return result.withReceipt(Response.json({ status: 'unlocked' }))
}
```

#### Client accesses the resource (zero-dollar auth)

Subsequent requests use zero-dollar auth. The server checks the client's identity against the access list.

```ts [server.ts]
export async function accessHandler(request: Request) {
  const result = await mppx.charge({ amount: '0' })(request) // [!code hl]
  if (result.status === 402) return result.challenge

  const credential = Credential.fromRequest(request)
  if (!hasAccess({ dataset: 'premium', owner: credential.source })) {
    return Response.json({ error: 'Not unlocked' }, { status: 403 })
  }

  return result.withReceipt(Response.json({ data: getDataset('premium') }))
}
```

::::

### Case study: multi-step agent workflow

An agent orchestrates a pipeline where one paid step kicks off several follow-up steps that only need identity. Each step verifies the same public key to maintain continuity across the workflow.

::::steps

#### Agent starts the pipeline (paid)

The agent pays to kick off generation. The server returns a pipeline ID tied to the agent's public key.

```ts [server.ts]
export async function createPipelineHandler(request: Request) {
  const result = await mppx.charge({ amount: '5.00' })(request)
  if (result.status === 402) return result.challenge

  const credential = Credential.fromRequest(request)
  const pipelineId = createPipeline({ owner: credential.source }) // [!code hl]

  return result.withReceipt(Response.json({ pipelineId }))
}
```

#### Agent retrieves intermediate results (zero-dollar auth)

The agent polls each stage of the pipeline. Every request proves the same identity without additional payment.

```ts [server.ts]
export async function stageHandler(request: Request) {
  const result = await mppx.charge({ amount: '0' })(request) // [!code hl]
  if (result.status === 402) return result.challenge

  const credential = Credential.fromRequest(request)
  const pipeline = getPipeline(pipelineIdFromUrl(request))

  if (pipeline.owner !== credential.source) {
    return Response.json({ error: 'Not your pipeline' }, { status: 403 })
  }

  const stage = pipeline.stages[stageFromUrl(request)]
  return result.withReceipt(Response.json({ output: stage.output, status: stage.status }))
}
```

#### Agent downloads the final artifact (zero-dollar auth)

The final download also uses zero-dollar auth—the server already collected payment at the start.

```ts [server.ts]
export async function resultHandler(request: Request) {
  const result = await mppx.charge({ amount: '0' })(request) // [!code hl]
  if (result.status === 402) return result.challenge

  const credential = Credential.fromRequest(request)
  const pipeline = getPipeline(pipelineIdFromUrl(request))

  if (pipeline.owner !== credential.source) {
    return Response.json({ error: 'Not your pipeline' }, { status: 403 })
  }

  return result.withReceipt(Response.json({ result: pipeline.finalResult }))
}
```

::::
