---
title: "Simulate an inbound travel-rule exchange with a counterparty VASP (non-production only)"
method: POST
path: "/v1/payments/simulate-travel-rule-inbound"
tags: ["Payments"]
---

# Simulate an inbound travel-rule exchange with a counterparty VASP (non-production only)

`POST /v1/payments/simulate-travel-rule-inbound`

## Request

- Base URL: `https://api.tesser.xyz`
- URL: `https://api.tesser.xyz/v1/payments/simulate-travel-rule-inbound`
- Auth: HTTP bearer
- TypeScript: <https://skmtc.dev/tesser/apis/tesser-api/docs/v1/payments/simulate-travel-rule-inbound/post.ts> (a function with typed inputs; its response is checked against the schema)

Has a Tesser-operated simulator VASP originate a real Notabene travel-rule transfer into one of your managed wallets, so you can rehearse your travel-rule handling without a second VASP. Where the transfer is authorized, testnet USDC is broadcast shortly after this responds and the destination balance is credited a few seconds later; a rejected transfer moves no money. Use POST /v1/payments/simulate-inbound to simulate a credited deposit without a travel-rule exchange. Not available in production.

## Request body

- SimulateTravelRuleInboundRequest
  - `to_account` string, uuid, required: Managed account ID the simulated counterparty should claim to be paying.
  - `claimed_beneficiary_name` string: Name the simulated counterparty claims for the beneficiary. Omitted, it claims the destination owner's own name and the transfer is authorized; supplied and wrong, name matching refuses it.
  - `network` 'BASE_SEPOLIA' | 'ETHEREUM_SEPOLIA' | 'POLYGON_AMOY': EVM testnet the simulated transfer settles on. Defaults to BASE_SEPOLIA.
  - `presentation` 'full' | 'partial': How completely the simulated counterparty satisfies your presentation policies. `full` answers the only policy raised. `partial` raises a second policy the counterparty can never answer alongside it, so the exchange comes back partially satisfied. Defaults to `full`.
  - `mocked_risk_status` 'automatically_approved' | 'awaiting_decision' | 'automatically_rejected': Forces the risk_status of the screening that runs once the simulated money arrives. Omitted, the inbound is screened for real. Only meaningful on the authorized branch — a rejected transfer sends no money and is never screened.

## Response `201`

The compliance round trip finished and the inbound payment exists. This is not by itself a sign the transfer was authorized — a REJECTED transfer also produces a payment here. In both cases the step stays `created`, because its status only follows the money. Read the decision from the step's `status_reasons` on GET /v1/payments/{id}: `transfers-9216` where the transfer was authorized, `transfers-9215` or `transfers-9203` where it was rejected. The last one is in force. `provider_metadata` carries the raw Notabene transfer, whose own status is not updated for every decision Tesser takes, and `participants` carries the name match once `payment.participants_updated` publishes it, right after `payment.created`. Where the transfer was authorized, testnet USDC is broadcast just after this response and the payment confirms and credits a few seconds later, which `payment.updated` announces.

- object
  - `data` object, required
    - `id` string, required: Unique payment identifier
    - `workspace_id` string, required: Workspace ID the payment belongs to
    - `organization_reference_id` string, nullable: External reference ID for tracking.
    - `payment_info` PaymentInfo: Optional caller intent about why a payment is being sent. Both fields optional.
      - `payment_description_wire` string: Free-text description forwarded to the payout provider (max 140 chars); appears on the beneficiary's bank statement.
      - `payment_reason_code` string: Short code classifying the payment's purpose. Stored by Tesser, not forwarded to the provider.
    - `direction` 'inbound' | 'outbound' | 'rebalance', required: Payment direction.
    - `funding_account_id` string, nullable, required: Funding account ID. Payment-only field. Top-level (not part of any overlay).
    - `desired` PaymentDesiredOverlay, required: Caller's stated intent at creation. Frozen except by the two-step PATCH flow which fills account IDs.
      - `from` PaymentDesiredFromLegResponse, required: Source side.
        - `account_id` string, nullable, required: Account ID.
        - `amount` string, nullable, required: Amount.
        - `currency` string, nullable, required: Currency code.
        - `network` string, nullable, required: Blockchain network.
      - `to` PaymentDesiredToLegResponse, required: Destination side.
        - `account_id` string, nullable, required: Account ID.
        - `amount` string, nullable, required: Amount.
        - `currency` string, nullable, required: Currency code.
        - `network` string, nullable, required: Blockchain network.
    - `estimated` PaymentEstimatedOverlay, required: Tesser's projection for how the payment will proceed.
      - `from` PaymentEstimatedFromLeg, required: Source side.
        - `account_id` string, nullable, required: Account ID.
        - `amount` string, nullable, required: Amount.
        - `currency` string, nullable, required: Currency code.
        - `network` string, nullable, required: Blockchain network.
      - `to` PaymentEstimatedToLeg, required: Destination side.
        - `account_id` string, nullable, required: Account ID.
        - `amount` string, nullable, required: Amount.
        - `currency` string, nullable, required: Currency code.
        - `network` string, nullable, required: Blockchain network.
    - `actual` PaymentActualOverlay, required: What actually settled. Populated once at least one step has `status: completed`.
      - `from` PaymentActualFromLeg, required: Source side.
        - `account_id` string, nullable, required: Account ID.
        - `amount` string, nullable, required: Amount.
        - `currency` string, nullable, required: Currency code.
        - `network` string, nullable, required: Blockchain network.
      - `to` PaymentActualToLeg, required: Destination side.
        - `account_id` string, nullable, required: Account ID.
        - `amount` string, nullable, required: Amount.
        - `currency` string, nullable, required: Currency code.
        - `network` string, nullable, required: Blockchain network.
    - `risk_status` 'unchecked' | 'awaiting_decision' | 'automatically_approved' | 'manually_approved' | 'automatically_rejected' | 'manually_rejected' | 'null', nullable, required: Risk assessment status. Cross-ramp payments are not risk-screened: `null` when no status is stored, otherwise the stored status.
    - `risk_status_reasons` RiskStatusReason[], required: Reasons `risk_status` was assigned to the specific outcome (`automatically_approved`, `awaiting_decision`, `automatically_rejected`).
      - `risk_status_reason_type` 'ENTITY' | 'ACCOUNT' | 'OWNERSHIP' | 'COUNTERPARTY' | 'INDIRECT' | 'BEHAVIORAL' | 'ERROR', required: Type of risk identified for the screened wallet or wallets it interacted with. `OWNERSHIP`: risk from the screened wallet's own activity. `COUNTERPARTY`: a wallet the screened wallet directly interacted with. `INDIRECT`: wallets the screened wallet had indirect exposure to (two or more hops away). `BEHAVIORAL`: the screened wallet's behavior demonstrates a pattern of money laundering or funds obfuscation. `ENTITY`: risk from the screened wallet's attributed entity, including allowlist and blocklist matches. `ACCOUNT`: the screened wallet address itself matched your allowlist; the reason points at the account holding it. `ERROR`: wallet screening could not be completed.
      - `risk_status_reason_category` string, required: The specific risk category within the type. For `risk_status_reason_type` = `OWNERSHIP`, `COUNTERPARTY`, or `INDIRECT`: one of the [supported risk categories](/overviews/wallet-screening-risk-categories#supported-risk-categories). For `BEHAVIORAL`: one of the [behavioral signatures](/overviews/wallet-screening-risk-categories#behavioral-signatures). For `ENTITY`: one of the supported risk categories when `risk_status_reason_severity` is `Low`, `Medium`, `High`, or `Severe`; the participant role (`ORIGINATOR`, `ORIGINATOR_VASP`, `BENEFICIARY`, `BENEFICIARY_VASP`, `CUSTODY_SERVICE`, or `INTERMEDIARY_VASP`) when `risk_status_reason_severity` is `ALLOWLISTED` or `BLOCKLISTED`. For `ACCOUNT`: the participant role of the screened wallet — `BENEFICIARY` on an outbound payment, `ORIGINATOR` on an inbound one. For `ERROR`: `SCREENING_FAILED` when wallet screening could not be completed, or `Token Contract` when the counterparty is a token contract.
      - `risk_status_reason_participant_id` string, nullable: Links this reason to the corresponding `participants[].resource.id`. Populated on `ENTITY` reasons, on the token-contract `ERROR` reason, and on `ACCOUNT` reasons (the account holding the allowlisted address); otherwise null. On an inbound payment it can name a participant that `participants[]` does not show until `payment.participants_updated` publishes it.
      - `risk_status_reason_message` string, required: Plain-English description of the risk category.
      - `risk_status_reason_severity` 'Low' | 'Medium' | 'High' | 'Severe' | 'ALLOWLISTED' | 'BLOCKLISTED', required: Severity of the signal: `Low`, `Medium`, `High`, or `Severe`. For an `ENTITY` reason recording an allowlist or blocklist match: `ALLOWLISTED` or `BLOCKLISTED`.
      - `risk_status_reason_significance` 'DECISIVE' | 'NON_DECISIVE' | 'SUPERSEDED_BY_ALLOWLIST' | 'SUPERSEDED_BY_BLOCKLIST', nullable: How the reason figured in the `risk_status` outcome. `DECISIVE`: drove the outcome. `NON_DECISIVE`: exceeded a threshold in your risk policy but did not drive the outcome (a more severe reason was identified). `SUPERSEDED_BY_ALLOWLIST` / `SUPERSEDED_BY_BLOCKLIST`: would have been decisive but for an allowlist or blocklist match. Null when not applicable.
      - `volume_usd_percent` string, nullable: Observed share of the wallet's volume for this risk indicator, as a percentage. `OWNERSHIP`, `COUNTERPARTY`, and `INDIRECT` reasons only; otherwise null.
      - `volume_usd_percent_threshold` string, nullable: Configured percentage-of-volume threshold for this risk indicator, if set; otherwise null.
      - `volume_usd_amount` string, nullable: Observed USD volume for this risk indicator. `OWNERSHIP`, `COUNTERPARTY`, `INDIRECT`, and `BEHAVIORAL` reasons only; otherwise null.
      - `volume_usd_amount_threshold` string, nullable: Configured USD-volume threshold for this risk indicator, if set; otherwise null.
      - `confidence_score` 'LOW_CONFIDENCE' | 'LOW_MODERATE_CONFIDENCE' | 'MODERATE_CONFIDENCE' | 'HIGH_MODERATE_CONFIDENCE' | 'HIGH_CONFIDENCE' | 'NEAR_CERTAINTY' | 'CERTAIN' | 'EXACT_MATCH', nullable: The observed confidence level of the entity attribution. Populated for `risk_status_reason_type` = `ENTITY` when `risk_status_reason_severity` = `ALLOWLISTED` or `BLOCKLISTED`, and for `risk_status_reason_type` = `ACCOUNT`. `null` otherwise. The wallet screening provider supplies `LOW_CONFIDENCE` through `NEAR_CERTAINTY`; `CERTAIN` is Tesser-defined and marks an identity supplied through a Travel Rule data provider rather than inferred by the wallet screening provider; `EXACT_MATCH` is Tesser-defined and marks an `ACCOUNT` reason — the screened wallet address itself is on your allowlist, so no attribution was involved.
      - `confidence_score_threshold` 'LOW_CONFIDENCE' | 'LOW_MODERATE_CONFIDENCE' | 'MODERATE_CONFIDENCE' | 'HIGH_MODERATE_CONFIDENCE' | 'HIGH_CONFIDENCE' | 'NEAR_CERTAINTY' | 'CERTAIN', nullable: The confidence level the entity attribution must exceed in order for an entity to be eligible for allowlisting or blocklisting. Populated only for `risk_status_reason_type` = `ENTITY` when `risk_status_reason_severity` = `ALLOWLISTED` or `BLOCKLISTED`. `null` otherwise, including on an `ACCOUNT` reason: an address match has no confidence bar.
    - `provider_metadata` object[], required: Provider-defined responses for the payment, one entry per provider, passed through for analytics and audit. Wallet screening data can reference chains other than the payment network; use the payment's estimated or actual network fields for routing and risk_status, risk_status_reasons, and participants for Tesser's normalized screening outcome.
      - `provider_metadata_type` 'WALLET_RISK_SCREENING' | 'TRAVEL_RULE': What the payload is.
      - `provider_metadata_key` 'TRM_LABS' | 'NOTABENE': The provider that produced it: `TRM_LABS` (wallet screening) or `NOTABENE` (Travel Rule data).
      - `provider_metadata_data` object: The raw provider payload: the wallet screening provider's response (provider internal URL fields removed) or the Notabene transfer record. Wallet screening covers all supported chains on which an address exists, so provider-defined fields such as entities[].entity can reference a chain other than the payment network.
    - `participants` object[], required: Participants Tesser identified in the flow of funds, with the record each was reconciled to. On an inbound payment, empty until `payment.participants_updated` publishes them.
      - `role` 'ORIGINATOR' | 'ORIGINATOR_VASP' | 'BENEFICIARY' | 'BENEFICIARY_VASP' | 'CUSTODY_SERVICE' | 'INTERMEDIARY_VASP' | 'CONTRACT' | 'ORIGINATOR_UPDATED' | 'ORIGINATOR_VASP_UPDATED' | 'BENEFICIARY_UPDATED': The participant's role in the flow of funds. On an inbound payment, `ORIGINATOR_UPDATED`, `ORIGINATOR_VASP_UPDATED` and `BENEFICIARY_UPDATED` mark data added after the participants were published, for a role that was already there; earlier entries never change.
      - `resource` object: The record the participant was reconciled to and the reconciliation outcome.
        - `resource_type` 'COUNTERPARTY' | 'TENANT' | 'WORKSPACE' | 'ACCOUNT': The kind of record the participant was reconciled to: `COUNTERPARTY` (most common); `TENANT` (the sending or receiving wallet belongs to one of your tenants directly); `WORKSPACE` (one of your organization's own wallets); or `ACCOUNT` (the account holding a wallet address on your allowlist — see `risk_status_reason_type` = `ACCOUNT`).
        - `id` string, nullable: Identifier of the record the participant was reconciled to — a counterparty, tenant, workspace, or account ID, per `resource_type`.
        - `name_match_score` string, nullable: The name-match score, 0–100. Null where no name matching applied.
        - `name_match_threshold` string, nullable: The lowest score that counts as a match; the comparison is inclusive, so a score equal to the threshold matches. Null where no name matching applied.
        - `name_match_comparison_id` string, nullable: ID of the record whose name most closely matched the supplied name — the record the score was computed against. Null where no name matching applied.
        - `resolution` 'EXACT_MATCH' | 'PARTIAL_MATCH' | 'DID_NOT_MATCH' | 'COUNTERPARTY_CREATED' | 'USER_SUPPLIED_ID': The reconciliation outcome.
    - `risk_reviewed_by` string, nullable: User ID who reviewed the risk.
    - `risk_reviewed_at` string, date-time, nullable: Timestamp when risk was reviewed.
    - `risk_review_reason` string, nullable: Reason the reviewer gave when approving or rejecting the payment. Null when none was given or the payment was not manually reviewed.
    - `balance_status` 'unreserved' | 'awaiting_funds' | 'reserved', nullable, required: Balance reservation status.
    - `balance_reserved_at` string, date-time, nullable, required: Timestamp when balance was reserved.
    - `steps` PaymentStep[], required: Payment execution steps.
      - `id` string, required: Unique step identifier
      - `payment_id` string, required: ID of the parent payment
      - `step_sequence` integer, required: Order of this step in the payment flow (1-based)
      - `step_type` 'transfer' | 'swap', required: Type of step
      - `estimated` PaymentStepEstimatedOverlay, required: Planned values for this step.
        - `from` PaymentStepEstimatedFromLeg, required: Source side.
          - `account_id` string, nullable, required: Account ID.
          - `amount` string, nullable, required: Amount.
          - `currency` string, nullable, required: Currency code.
          - `network` string, nullable, required: Blockchain network.
        - `to` PaymentStepEstimatedToLeg, required: Destination side.
          - `account_id` string, nullable, required: Account ID.
          - `amount` string, nullable, required: Amount.
          - `currency` string, nullable, required: Currency code.
          - `network` string, nullable, required: Blockchain network.
      - `actual` PaymentStepActualOverlay, required: Settled values once the step reaches `status: completed`. All-null until then; remains all-null on failure.
        - `from` PaymentStepActualFromLeg, required: Source side.
          - `account_id` string, nullable, required: Account ID.
          - `amount` string, nullable, required: Amount.
          - `currency` string, nullable, required: Currency code.
          - `network` string, nullable, required: Blockchain network.
        - `to` PaymentStepActualToLeg, required: Destination side.
          - `account_id` string, nullable, required: Account ID.
          - `amount` string, nullable, required: Amount.
          - `currency` string, nullable, required: Currency code.
          - `network` string, nullable, required: Blockchain network.
      - `transaction_hash` string, nullable: Blockchain transaction hash
      - `unsigned_transaction` string, nullable: Unsigned transaction bytes for client signing. Present only for Turnkey-signed steps in `signature_requested` status.
      - `fees` StepFee[], required: Fees associated with this step.
        - `fee_amount` string, required: Fee amount.
        - `fee_currency` string, required: Fee currency code.
        - `fee_type` string, required: Fee type (e.g., 'gas', 'provider', 'tesser_fee').
        - `fee_metadata` object: Additional metadata for the fee (JSON object).
      - `provider_key` 'capa' | 'circle_mint' | 'kraken' | 'openfx' | 'turnkey' | 'zuba', nullable: Provider used for this step
      - `status` 'created' | 'signature_requested' | 'signed' | 'submitted' | 'confirmed' | 'completed' | 'failed', required: Current status of this step
      - `status_reasons` StatusReason[], nullable: Reasons for the current status
        - `error_code` string, required
        - `error_message` string, required
      - `created_at` string, date-time, required: Step creation timestamp
      - `updated_at` string, date-time, required: Step last update timestamp
      - `signature_requested_at` string, date-time, nullable: Timestamp when a signature was requested for the step
      - `signed_at` string, date-time, nullable: Timestamp when the step was cryptographically signed
      - `submitted_at` string, date-time, nullable: Timestamp when step was submitted
      - `confirmed_at` string, date-time, nullable: Timestamp when step was confirmed
      - `completed_at` string, date-time, nullable: Timestamp when step completed
      - `failed_at` string, date-time, nullable: Timestamp when step failed
    - `created_at` string, required: ISO 8601 timestamp of creation.
    - `updated_at` string, required: ISO 8601 timestamp of last update.
    - `expires_at` string, date-time, nullable, required: ISO 8601 timestamp when the payment expires.
    - `travel_rule` object
      - `transfer_id` string, required: The Notabene transfer the simulated counterparty created. Read the decision itself, the name-matching check and the settlement hash from GET /v1/payments/{id}.
      - `is_travel_rule` boolean, required: Whether Notabene flagged the transfer as travel-rule. False means no decision will follow.
      - `presentation` 'full' | 'partial', required: The rehearsal that was run, echoing the request. Partial means a second presentation policy was raised that the counterparty can never answer, so Notabene reports the presentation partially satisfied.
      - `presentation_answered` boolean, required: Whether the counterparty's PII reached your policy. False means no decision will follow and no money will be sent.

## Other responses

- `202`: The travel-rule transfer exists but the decision had not landed within the wait budget. This is not a failure and the money leg is unaffected: once Notabene decides, an authorized transfer is still broadcast and still credits. Poll GET /v1/payments until travel_rule.transfer_id resolves to a payment. is_travel_rule false means Notabene never flagged the transfer, so no decision will follow; presentation_answered false means the counterparty's PII never reached your policy, and nothing will be sent.

## Changes

- **2026-11-09** `46461e694e2a`: 3 warning
  - added the new `BENEFICIARY_UPDATED` enum value to the `data/allOf[#/components/schemas/Payment]/participants/items/role` response property for the response status `201`
  - added the new `ORIGINATOR_UPDATED` enum value to the `data/allOf[#/components/schemas/Payment]/participants/items/role` response property for the response status `201`
  - added the new `ORIGINATOR_VASP_UPDATED` enum value to the `data/allOf[#/components/schemas/Payment]/participants/items/role` response property for the response status `201`
- **2026-11-07** `14f951e986b1`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/tesser/apis/tesser-api/changes/v1/payments/simulate-travel-rule-inbound/post.md)

---

[API](https://skmtc.dev/tesser/apis/tesser-api.md) - [All operations](https://skmtc.dev/tesser/apis/tesser-api/llms.txt) - [OpenAPI document](https://skmtc.dev/tesser/apis/tesser-api/revisions/46461e694e2a?raw)
