> ## Documentation Index
> Fetch the complete documentation index at: https://docs.madra.finance/llms.txt
> Use this file to discover all available pages before exploring further.

# Lifecycle Hooks

> Hook into the verify and settle lifecycle to add logging, custom validation, or side effects.

The middleware exposes hooks at three points in the payment lifecycle. Use them for logging, custom validation, and side effects. Do not use them to mutate protocol fields.

## Hook points

| Hook                | Fires                                             | Can reject? |
| ------------------- | ------------------------------------------------- | ----------- |
| `onPaymentRequired` | Before returning a 402                            | No          |
| `onVerify`          | After facilitator verify, before serving resource | Yes         |
| `onSettle`          | After facilitator settle                          | No          |

## Example

```typescript theme={null}
requirePayment({
  ...,
  hooks: {
    onPaymentRequired: async ({ req, requirements }) => {
      // Log the quote or bump the price for premium users
      metrics.increment("402.emitted", { path: req.path });
    },
    onVerify: async ({ req, verification, requirements }) => {
      // Enforce a business rule beyond the payment itself
      if (isBlockedAccount(verification.from)) {
        throw new PaymentRejection("account_blocked");
      }
    },
    onSettle: async ({ verification, settlement }) => {
      await db.record({
        tx: settlement.txHash,
        from: verification.from,
        amount: settlement.amount,
      });
    },
  },
});
```

## Rules

* Hooks must be idempotent. `onSettle` may be called more than once for the same tx if a retry occurs; keys writes with `txHash`.
* `onVerify` is the only hook that can reject. Throwing `PaymentRejection(code)` returns a `402` with the given code to the client.
* Do not mutate `requirements` from `onPaymentRequired`. Amounts and destinations must be stable across the verify cycle.
* Hooks have a 500ms soft budget. Blocking the request path for longer degrades verify latency.

## Rejection codes for custom rejections

Use codes prefixed with `custom_` to avoid colliding with the [standard rejection set](/reference/error-codes). Buyers treat unknown `custom_*` codes as terminal.
