Simulate a Travel Rule Inbound Payment
POST /v1/payments/simulate-travel-rule-inbound has a Tesser-operated simulator VASP originate a real Notabene Travel Rule transfer into one of your managed wallets, so you can test your integration's handling of an inbound Travel Rule exchange without standing up a second VASP.
This simulates the compliance exchange, not the money
This endpoint does not move money. No on-chain transfer is broadcast, no balance changes, and the payment's pending step stays status: created — it does not progress to confirmed or completed. If you need a credited balance to test against, use POST /v1/payments/simulate-inbound instead. Calling this endpoint and expecting your balance to change is the most common way to misread it.
Everything about the compliance decision itself is genuine: a real Notabene transfer is created, real PII is exchanged, and your organization's own name-matching and authorization logic decides the outcome — the same code path a real counterparty-initiated inbound runs through. Only the counterparty and the settlement that would have followed it are simulated.
Prerequisites
- Sandbox or staging only. The endpoint is unavailable in production, gated the same way as
simulate-inbound. Calling it in production returnspayments-3053. - The
notabene-travel-rule-inboundfeature flag must be enabled for your organization. Without it, the webhooks that carry the compliance decision are received and dropped, so the call would otherwise hang until it times out — the endpoint checks the flag up front instead and fails fast withpayments-3083. Contact Tesser support to have the flag enabled for your organization. - Your organization must have its Travel Rule VASP identity configured with Tesser. This is set up during onboarding; if it is missing, the call fails with
payments-3085. to_accountmust be a managedstablecoin_ethereumwallet in your workspace. Creating a managed wallet registers it with Notabene automatically — no separate registration step. An account that does not exist, isn't managed, or isn't astablecoin_ethereumwallet fails the call (payments-1002/payments-3055).
You never see or supply any Notabene credentials — the simulator VASP's identity and your organization's own Travel Rule identity are both handled server-side.
See Authentication for how to obtain the bearer token ($CLIENT_TOKEN below).
Request
| Field | Required | Description |
|---|---|---|
to_account | Yes | The managed wallet account ID the simulated counterparty claims to be paying. |
claimed_beneficiary_name | No | The name the simulated counterparty claims for the beneficiary. Omit it to have the simulator claim the destination account owner's own name — the transfer is authorized. Supply a name that doesn't match, and Tesser's own name-matching rejects the transfer, which is how you rehearse your refusal path. |
network | No | One of BASE_SEPOLIA, ETHEREUM_SEPOLIA, POLYGON_AMOY. Defaults to BASE_SEPOLIA. |
The request never accepts mocked_risk_status — see Risk status is not simulated here.
The authorized call
Omit claimed_beneficiary_name and the simulator claims the destination wallet owner's actual name, so the name match succeeds and the transfer is authorized:
Code
Once the compliance round trip finishes and the resulting payment exists, the endpoint responds 201:
Code
travel_rule sits alongside the payment fields inside data — it is not a separate resource. Fields:
| Field | Description |
|---|---|
transfer_id | The Notabene transfer ID the simulator created. It is not a Tesser payment ID and cannot be passed to GET /v1/payments/{id}, nor filtered on — see The 202 response for how to find the payment it becomes. |
is_travel_rule | Whether Notabene flagged the simulated transfer as Travel Rule. Notabene sets this shortly after the transfer is created; false means either it never did within the wait budget or reading the transfer was permanently refused — either way, no compliance decision will follow. |
presentation_answered | Whether the simulated counterparty's PII reached your organization's Travel Rule policy. false means the PII exchange itself failed — no name-matching decision was made. |
status | The transfer's status at Notabene, re-read after the simulator tried to settle it. SETTLED is the normal authorized path — Notabene authorized the transfer and the simulator's settlement went through. AUTHORIZED means Notabene authorized it but either the settle call or the re-read afterward did not go through; settlement_id is then null. REJECTED means your organization's name matching refused the claim. null means the transfer's state could not be read within the wait budget. |
name_matching_checked | Whether a beneficiary-name-matching check was recorded for the transfer. |
settlement_id | The fabricated settlement identifier the simulator used to drive the transfer to SETTLED, or null if the simulated settlement did not go through (for example, because the transfer was rejected — a rejected transfer is never settled — or because the settle call itself failed). No transaction was ever broadcast; this value only exists to make Notabene's settlement webhook fire, which stamps it onto the payment's pending step — see below for what that means for transaction_hash. |
201 is not proof of authorization
A 201 response means the compliance round trip finished — it does not mean it finished with authorization. A rejected transfer also produces a payment here, with its step failed rather than pending. Always read travel_rule.status for the actual decision, not just the HTTP status code.
The missing transaction hash
transaction_hash: null in the response above is expected, and stays that way even after settlement. The simulator settles the Notabene transfer with a fabricated CAIP-220 settlement id (travel_rule.settlement_id) rather than a real on-chain transaction, purely to make Notabene fire its settlement webhook. That webhook arrives just after this response and stamps the fabricated hash onto the payment's pending step — so a subsequent GET /v1/payments/{id} shows a step that carries a transaction_hash while its status is still created. Nothing was ever broadcast, so nothing will ever confirm it: that is the resting state by design, not a sign of something stuck.
The refusal call
Supply a claimed_beneficiary_name that does not match the destination wallet's owner, and Tesser's own name-matching logic refuses the transfer — the same decision it would make against a genuine counterparty:
Code
The simulated counterparty claims that wrong name as the beneficiary. Your organization's name-matching compares it against the destination account owner's actual name, does not find a match, and rejects the transfer. The response is still 201 — the compliance round trip finished, it just finished with a rejection:
Code
settlement_id is null here — a rejected transfer is never settled. The pending step's status is failed, not created; see Payment Participants for how a rejected Beneficiary name-match generally surfaces on a payment's participants[].
The 202 response
The compliance round trip — Notabene flagging the transfer as Travel Rule, the PII exchange, and your organization's name-matching decision — can take longer than the request's wait budget (about 40 seconds). When it does, the endpoint responds 202 with what it knows so far, rather than blocking further:
Code
Nothing is settled on this path — settlement_id is always null in a 202 response, and the payment does not exist yet, so this response carries no payment_id to poll on. is_travel_rule: false means either Notabene never flagged the simulated transfer as Travel Rule within the wait budget or reading the transfer was permanently refused, so no decision — and no payment — will ever follow for that transfer; presentation_answered: false means the simulated counterparty's PII never reached your organization's policy.
Do not re-issue the same request to find the earlier payment: every call creates a brand-new Notabene transfer and a new simulated inbound, so retrying can never surface the one you are waiting on, only add another. There is also no supported way to look the earlier transfer up by ID — GET /v1/payments's filters (start_date, end_date, direction) don't include a transfer or provider-reference filter. Once the compliance decision lands, get the resulting payment one of two ways:
- Subscribe to the
payment.createdwebhook, if you receive webhooks in this environment, and read the payment ID off the event. - List inbound payments (
GET /v1/payments?direction=inbound) and match thetransfer_idyou got from your original request or202response againstprovider_metadata[].provider_metadata_data["@id"]on each result yourself.travel_ruleis only present in this endpoint's own response —GET /v1/paymentsdoes not return it.
Risk status is not simulated here
The request does not accept mocked_risk_status. Sending it is rejected outright, because risk screening runs on inbound money, and this endpoint never produces any — there is nothing for a risk verdict to attach to. To rehearse a chosen risk outcome, use POST /v1/payments/simulate-inbound instead.
simulate-travel-rule-inbound vs simulate-inbound
Two separate simulators cover two separate halves of an inbound payment:
simulate-travel-rule-inbound | simulate-inbound | |
|---|---|---|
| Simulates | The Travel Rule compliance exchange | The money |
| Counterparty | A real Notabene transfer from a Tesser-operated simulator VASP | N/A — funds come from the platform faucet |
| On-chain transfer | None. Nothing is ever broadcast. | A real test-USDC transfer, broadcast on-chain |
| Balance change | None | Yes — the destination wallet is credited once the transfer confirms |
| Pending step | Stays created (or moves to failed on a rejection) | Progresses to confirmed, then completed |
| Risk outcome | Not applicable — mocked_risk_status is rejected | Chosen via mocked_risk_status |
| What you control | claimed_beneficiary_name, to force authorization or rejection | mocked_risk_status, to force a risk verdict |
If you need to test both — a payment whose Travel Rule exchange you control and whose balance actually moves — call both endpoints; they are independent and neither substitutes for the other.
Errors
| Error Code | HTTP Status | Meaning |
|---|---|---|
payments-1002 | 404 | to_account did not resolve to an account in this workspace. |
payments-3053 | 400 | Simulation is not available in production. |
payments-3054 | 400 | An unsupported network value, from an internal configuration gap rather than the request itself — a network outside BASE_SEPOLIA, ETHEREUM_SEPOLIA, POLYGON_AMOY is rejected by request validation before this error can fire. |
payments-3055 | 400 | to_account exists but is not a managed stablecoin_ethereum wallet with an on-chain address. |
payments-3083 | 400 | The notabene-travel-rule-inbound feature flag is not enabled for your organization. Contact Tesser support to have it enabled. |
payments-3084 | 503 | The simulator VASP is not configured in this environment. This is an environment issue on Tesser's side, not something a request can fix. |
payments-3085 | 400 | Your organization has no Travel Rule VASP identity configured with Tesser. |
payments-3086 | 400 | The destination account resolved to no usable beneficiary name to claim. Supply claimed_beneficiary_name, or give the account's entity a name. |
See the Errors reference for the full Payments error table, and Compliance and Risk Management for how Travel Rule and wallet risk screening apply across all payment directions.