<!-- tempo-docs-context -->
> Source: [/docs/guide/payments/configure-receive-policies](https://tempo.xyz/developers/docs/guide/payments/configure-receive-policies.md)
> Section: [Accounts](https://tempo.xyz/developers/docs/accounts.md). Create accounts, connect wallets, read balances, send and receive payments, and reconcile deposits.
> [Documentation index](https://tempo.xyz/developers/llms.txt) · [Docs skill](https://tempo.xyz/developers/SKILL.md)

> Tempo MCP: Use `search`, `find_pages`, `read_page`, and `code` at `https://mcp.tempo.xyz` for Tempo and related documentation.
> If an indexed page is missing or reflects an older site, read this site’s [documentation index](https://tempo.xyz/developers/llms.txt) and Markdown pages directly.

<span id="configure-receive-policies" />

<span id="control-incoming-payments" />

# Receive policies

Configure an address-level receive policy to choose which [TIP-20 Tokens](https://tempo.xyz/developers/get-started/stablecoins) an account accepts and which senders may pay it. Choose a recovery authority for payments the policy blocks.

If a receive policy blocks an inbound transfer or mint, the TIP-20 call still succeeds. The funds are credited to `ReceivePolicyGuard` instead of the receiver, and `ReceivePolicyGuard` records a receipt that can be claimed later by the configured recovery authority.

The token's [TIP-403](https://tempo.xyz/developers/docs/protocol/tip403/spec) policy checks still run first and still revert on failure. Only a receive-policy failure redirects delivery to `ReceivePolicyGuard`.

For the full protocol flow, claim rules, burn rules, and event signatures, see the [Receive Policies protocol overview](https://tempo.xyz/developers/docs/protocol/tip403/receive-policies).

## Try receive policies

Create a temporary test account, allow AlphaUSD, and compare delivery with a blocked BetaUSD transfer. Then recover the held payment with the receiving account. The demo uses test tokens on Moderato and keeps its signing key in this tab's memory.

**Interactive demo: Receive policies**

1. Create a temporary Moderato account, accept AlphaUSD, hold BetaUSD, and recover the blocked payment.

## When to configure a receive policy

Configure a receive policy when an address should reject some incoming TIP-20 tokens or some incoming senders.

For example, receive policies are useful when:

* a regulated entity that only wants to receive from addresses held by individuals it has KYC'ed
* an orchestrator or exchange that only wants specific tokens sent to its deposit addresses

<span id="account-level-receive-policies" />

## Understand receive policy behavior

If an address has no receive policy, all transfers and mints are allowed by the receive-policy layer.

| Result | Transaction result | Where the funds go |
|---|---|---|
| Token-level checks fail | The call reverts | Funds are not delivered |
| Receive policy allows | The call succeeds | Receiver is credited normally |
| Receive policy blocks | The call succeeds | `ReceivePolicyGuard` is credited and records a receipt |

The receive-policy check applies to `transfer`, `transferFrom`, `transferWithMemo`, `transferFromWithMemo`, `systemTransferFrom`, `mint`, and `mintWithMemo`.

It does not apply to `approve`, `permit`, or `burn`. It also does not affect fee deposits or refunds through `transfer_fee_pre_tx` or `transfer_fee_post_tx`, TIP-20 rewards, or internal balances.

For `transferFrom`, the spender's allowance is consumed even when the receiver's policy blocks delivery, because the funds still move out of the `from` account.

## Configure the policy

This example accepts only AlphaUSD from any sender on Moderato testnet and lets the receiving account recover blocked payments. Start with a funded test account and the [client setup](https://tempo.xyz/developers/docs/accounts/examples). Use a current Viem version with `client.receivePolicy` support.

In TypeScript, `client` is the configured Viem client, `receiver` is the receiving account address, and `alphaUsd` is `0x20c0000000000000000000000000000000000001`. The Wagmi examples run inside a component using the same network and receiving account; invoke write handlers from a button and show pending, confirmation, and error states.

For CLI examples, use the Foundry setup's `TEMPO_RPC_URL`, `TEMPO_PRIVATE_KEY`, `TEMPO_ACCOUNT_ADDRESS`, and `ALPHA_USD`. Set the registry address once:

```bash
export TIP403_REGISTRY=0x403c000000000000000000000000000000000000
```

::::steps
### Create or choose a sender policy

A receive policy uses `senderPolicyId` to decide which senders are allowed. The sender policy can be:

* built-in policy `0`, which rejects all
* built-in policy `1`, which allows all
* an existing simple TIP-403 `WHITELIST` or `BLACKLIST` policy
* a newly created simple TIP-403 `WHITELIST` or `BLACKLIST` policy

`COMPOUND` policies are not valid for `senderPolicyId`. This example uses policy `1` to allow any sender; substitute a sender allowlist to restrict counterparties.

### Create or choose a token filter

A receive policy uses `tokenFilterId` to decide which TIP-20 tokens are allowed. Token filters use existing TIP-403 policy data and membership sets, where members are interpreted as TIP-20 token addresses.

The token filter can use built-in policy `0`, built-in policy `1`, or a simple TIP-403 `WHITELIST` or `BLACKLIST` policy. `COMPOUND` policies are not valid for `tokenFilterId`.

Create a token allowlist containing AlphaUSD. The policy members are token contract addresses, not customer addresses.

:::code-group
```ts [Viem]
const { policyId: tokenPolicyId } = await client.policy.createSync({
  type: 'whitelist',
  addresses: [alphaUsd],
})
```

```bash [CLI]
cast send "$TIP403_REGISTRY" \
  'createPolicyWithAccounts(address,uint8,address[])' \
  "$TEMPO_ACCOUNT_ADDRESS" 0 "[$ALPHA_USD]" \
  --tempo.fee-token "$ALPHA_USD" \
  --rpc-url "$TEMPO_RPC_URL" --private-key "$TEMPO_PRIVATE_KEY"
```

```tsx [Wagmi]
import { Hooks } from 'wagmi/tempo'
const { mutate: createFilter, data: filter, isPending, error } = Hooks.policy.useCreateSync()
const createTokenFilter = () => createFilter({
  type: 'whitelist', addresses: [alphaUsd],
})
const tokenPolicyId = filter?.policyId
```
:::

Save the confirmed `policyId` for the next step. In the CLI receipt, it is the second topic (`topics[1]`) of `PolicyCreated(uint64,address,uint8)`; set `TOKEN_POLICY_ID` to that value. In Wagmi, wait for `filter` before enabling the next action. The CLI's policy type `0` means whitelist, distinct from the built-in policy ID `0` that rejects all.

### Choose a recovery authority

The recovery authority controls who may claim receipts for future blocked transfers or mints:

| Recovery authority | Who can claim |
|---|---|
| `address(0)` | the originator of the transfer or mint |
| the receiver's address | the receiver |
| another nonzero address | that address |

Changing the recovery authority affects future receipts only. Existing receipts keep the recovery authority captured when they were created.

Nonzero recovery authorities cannot be `ReceivePolicyGuard`, [virtual addresses](https://tempo.xyz/developers/docs/protocol/tip20/virtual-addresses), or system precompile addresses that cannot initiate calls.

### Set the receive policy

Call `setReceivePolicy(senderPolicyId, tokenFilterId, recoveryAuthority)` from the receiving account. Here the receiver also acts as recovery authority:

:::code-group
```ts [Viem]
await client.receivePolicy.setSync({
  senderPolicyId: 'allow-all',
  tokenPolicyId,
  claimer: 'self',
})
```

```bash [CLI]
cast send "$TIP403_REGISTRY" 'setReceivePolicy(uint64,uint64,address)' \
  1 "$TOKEN_POLICY_ID" "$TEMPO_ACCOUNT_ADDRESS" \
  --tempo.fee-token "$ALPHA_USD" \
  --rpc-url "$TEMPO_RPC_URL" --private-key "$TEMPO_PRIVATE_KEY"
```

```tsx [Wagmi]
import { useWriteContract } from 'wagmi'
import { Abis, Addresses } from 'viem/tempo'
const { writeContract, data: hash, isPending, error } = useWriteContract()
const setPolicy = () => tokenPolicyId !== undefined && writeContract({
  address: Addresses.tip403Registry, abi: Abis.tip403Registry,
  functionName: 'setReceivePolicy', args: [1n, tokenPolicyId, receiver],
})
```
:::

Viem names the token filter `tokenPolicyId` and maps `claimer: 'self'` to the signing account. The raw contract ABI calls these fields `tokenFilterId` and `recoveryAuthority`. Wait for the setting transaction to confirm before checking deposits.

If the receiver is using virtual addresses, configure the policy on the resolved master address. A virtual address must not call `setReceivePolicy(...)`.
::::

## Check a payment before sending

Set `sender` (or `TEMPO_SENDER` in the CLI) to the address sending the tokens. Check AlphaUSD first, then a token excluded from your allowlist. The receive-policy check should allow the first and block the second.

:::code-group
```ts [Viem]
const { authorized, blockedReason } = await client.receivePolicy.validate({
  token: alphaUsd, sender, receiver,
})
```

```bash [CLI]
cast call "$TIP403_REGISTRY" \
  'validateReceivePolicy(address,address,address)(bool,uint8)' \
  "$ALPHA_USD" "$TEMPO_SENDER" "$TEMPO_ACCOUNT_ADDRESS" \
  --rpc-url "$TEMPO_RPC_URL"
```

```tsx [Wagmi]
import { useReadContract } from 'wagmi'
import { Abis, Addresses } from 'viem/tempo'
const { data: validation } = useReadContract({
  address: Addresses.tip403Registry, abi: Abis.tip403Registry,
  functionName: 'validateReceivePolicy', args: [alphaUsd, sender, receiver],
})
```
:::

This checks only the address's receive policy, not token-level restrictions, balances, or the full transaction. Policies can change before execution, so always inspect the confirmed payment receipt. For a delegated `transferFrom`, `sender` is the token owner's address, not the spender.

## Monitor blocked receipts

`ReceivePolicyGuard` lives at `0xB10C000000000000000000000000000000000000`.

When delivery is blocked, the token balance is credited to that address. `ReceivePolicyGuard` stores a keyed amount for the blocked receipt. The receipt includes the token, recovery authority, originator, recipient, timestamp, nonce, blocked reason, inbound kind, and memo.

`ReceivePolicyGuard` does not enumerate receipts onchain. Claimers need the receipt bytes, usually by indexing `TransferBlocked` events.

The regular TIP-20 events still happen:

* a blocked transfer emits the regular `Transfer` event with `ReceivePolicyGuard` as the recipient, then `TransferBlocked(...)`
* a blocked mint emits the regular `Transfer` and `Mint` events with `ReceivePolicyGuard` as the recipient, then `TransferBlocked(...)`

Index these events to track policy changes and receipt lifecycle:

| Event | Purpose |
|---|---|
| `ReceivePolicyUpdated` | A receive policy changed for an address |
| `TransferBlocked` | A transfer or mint was blocked and a receipt was created |
| `ReceiptClaimed` | A receipt was consumed and funds were released |
| `ReceiptBurned` | A receipt was consumed and funds were burned |

`TransferBlocked.receipt` is the ABI-encoded receipt witness. The spec requires that this receipt be directly usable as the `receipt` argument to `balanceOf`, `claim`, and `burnBlockedReceipt`.

## Claim blocked receipts

A claim is the normal recovery path for blocked transfers or mints. It consumes one full receipt and releases the full amount to one destination. Partial claims are not supported.

Only the authorized claimer may call `claim(...)`:

* if the receipt's `recoveryAuthority` is `address(0)`, only the receipt's `originator` may claim
* otherwise, only the receipt's nonzero `recoveryAuthority` may claim

Changing a receiver's recovery authority does not change who can claim older receipts.

Use the exact encoded `TransferBlocked.receipt` as `blockedReceipt` (or `BLOCKED_RECEIPT` in the CLI). For the receiver-recovery policy above, sign as the receiving account and release to that same account:

:::code-group
```ts [Viem]
await client.receivePolicy.claimSync({ to: receiver, receipt: blockedReceipt })
```

```bash [CLI]
cast send 0xB10C000000000000000000000000000000000000 \
  'claim(address,bytes)' "$TEMPO_ACCOUNT_ADDRESS" "$BLOCKED_RECEIPT" \
  --tempo.fee-token "$ALPHA_USD" \
  --rpc-url "$TEMPO_RPC_URL" --private-key "$TEMPO_PRIVATE_KEY"
```

```tsx [Wagmi]
import { useWriteContract } from 'wagmi'
import { Abis, Addresses } from 'viem/tempo'
const { writeContract, data: hash, isPending, error } = useWriteContract()
const claimPayment = () => writeContract({
  address: Addresses.receivePolicyGuard, abi: Abis.receivePolicyGuard,
  functionName: 'claim', args: [receiver, blockedReceipt],
})
```
:::

This resumes delivery without rechecking the receiver's receive policy, but token-level authorization still applies. Only release a held payment after your application approves it. Other claim destinations have additional checks; see the [claim rules](https://tempo.xyz/developers/docs/protocol/tip403/receive-policies#claims).

## Burn blocked receipts

Burning is an issuer-only path for blocked receipts.

`burnBlockedReceipt(...)` consumes one full receipt and burns the stored amount from `ReceivePolicyGuard`.

The caller must hold `BURN_BLOCKED_ROLE` for the token. A receipt is burnable only when its policy subject is currently unauthorized as a sender under the token's TIP-403 policy.

Burn eligibility has additional policy checks. See the [protocol overview](https://tempo.xyz/developers/docs/protocol/tip403/receive-policies#burns) before implementing issuer burn flows.

## Virtual-address behavior

If `to` is a virtual address, it is resolved to its master address before checks run. Receive-policy checks use the master address.

If resolution fails, the operation reverts as before. If the transfer or mint is blocked, the receipt is recorded for the master address and preserves the original `to` for attribution.

## Learn more about receive policies

* [Receive policies specification](https://tips.sh/1028) — The approved specification for address-level receive policies.
* [Protocol Overview](https://tempo.xyz/developers/docs/protocol/tip403/receive-policies) — Technical reference for receive-policy evaluation, guard receipts, claims, and events.
* [Accept a Payment](https://tempo.xyz/developers/docs/guide/payments/accept-a-payment) — Verify incoming payments and listen for transfer events.
* [Use Virtual Addresses](https://tempo.xyz/developers/docs/guide/payments/virtual-addresses) — Understand how virtual addresses affect deposit attribution.
