> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://mpp.dev/api/mcp` to find what you need.

# Identity \[Verify agents and clients]

Require a verified identity before payment, check the payer before settlement, or use the MPP Credential itself as identity.

## Options

| Option | Verifies | Use it for |
|---|---|---|
| [**Email OTP**](/extensions/identity/email-otp) | An email inbox, or any fact a trusted issuer attests | Verified-address access without your own login |
| [**Agent signatures**](/extensions/identity/agent-signatures) | The agent or bot that signed the request | Bot recognition and agent-specific policy |
| [**Your own identity provider**](/extensions/identity/custom) | Your identity provider's API keys, sessions, or SSO, or your own protocol | Existing customer access policy |
| [**Payer identity**](/extensions/identity/payer) | The payer named in the MPP Credential | Ownership, zero-dollar auth, and payer allowlists |

## How identity checks run

Pass verifiers in a route's `identity` option. `mppx` runs each verifier at most once per request, before it issues a Challenge, so rejected callers never see payment terms.

```mermaid
sequenceDiagram
  participant Client
  participant Server
  Client->>Server: Request + identity proof
  Note over Server: Run request verifiers
  alt Rejected
      Server-->>Client: 401, 403, or 503
  else Valid
      Server-->>Client: 402 + Challenge
      Client->>Server: Credential + identity proof
      Note over Server: Run verifiers and payer check
      Server-->>Client: 200 + Receipt
  end

```

Every verifier returns one fixed status:

| Status        | Response             | Meaning                                                                   |
| ------------- | -------------------- | ------------------------------------------------------------------------- |
| `valid`       | Continues to payment | The proof verified. Read its value with `result.identity?.get(verifier)`. |
| `missing`     | 401                  | No proof was presented                                                    |
| `invalid`     | 401                  | A proof was presented but didn't verify                                   |
| `forbidden`   | 403                  | The proof verified, but your policy rejects the identity                  |
| `unavailable` | 503                  | The proof couldn't be checked, such as during an issuer outage            |

* When several verifiers reject, the highest-precedence status decides: `forbidden`, then `unavailable`, `invalid`, and `missing`. The headers of every rejection are merged. See [`Identity.Status`](/sdk/typescript/core/Identity.Status).
* A rejection arrives on the same branch as a Challenge, so `if (result.status === 402) return result.challenge` sends the rejection response. Read `result.rejection` (`stage`, `status`, `reason`) to log why.
* Routes without `identity` skip identity checks, not payment checks.

## Combine verifiers

Every verifier in a route's `identity` list must be valid. To accept any one of several, wrap them in [`Identity.anyOf`](/sdk/typescript/core/Identity.anyOf):

```ts twoslash [server.ts]
import type * as Tap from 'mppx/extensions/identity/tap'
import type * as WebBotAuth from 'mppx/extensions/identity/web-bot-auth'
import * as Identity from 'mppx/identity'
import { Mppx, tempo } from 'mppx/server'

declare const tap: ReturnType<typeof Tap.Server.verifier>
declare const webBotAuth: ReturnType<typeof WebBotAuth.Server.verifier>

const payment = Mppx.create({ methods: [tempo.charge()] })

export const handler = payment.charge({
  amount: '12.50',
  identity: [Identity.anyOf(tap, webBotAuth)], // [!code hl]
})
```

* Every `anyOf` member runs, so each presented proof's nonce is consumed. The first valid member wins.
* When every member rejects, the highest-precedence status decides and the headers of every rejection are merged.
* Attestation members are listed as one group of alternatives, such as `Identity-Attestation-Required: (email;issuer="https://identity.mpp.tempo.xyz" email;issuer="https://identity.example.com")`. The client meets the group with one attestation it holds, or acquires the first that succeeds, so accepting several issuers costs the caller one code.
