Transaction Screening

Submit your users' crypto transactions to Wirex for risk assessment, as required under the External Authorization model.

Before You Start

Read the following guides before proceeding:

GuideWhy
Getting StartedPlatform overview and setup
Api BasicsRequired headers and request configuration
AuthenticationHow to obtain access tokens
OverviewWhy balances and transactions live in your system

Overview

Under the standard integration model, crypto deposits and transfers move through Wirex-managed
wallets, so Wirex screens them for risk automatically as it processes them.

Under External Authorization, your system holds the balances and the transactions never reach Wirex.
Nothing is screened unless you submit it. This endpoint is how you submit it.

POST /api/v1/transactions/risk-assessment

Submitting these transactions is a regulatory requirement of the External Authorization model, not
an optional feature.
Wirex is obliged to screen the crypto activity of users onboarded through the
platform; on this model that obligation can only be met with data you provide. Treat the call as part
of your transaction pipeline, not as a reporting job that can be deferred or sampled.


What to Submit

Submit every crypto deposit and every crypto transfer belonging to a user you onboarded through
Wirex, once the transaction is confirmed on-chain.

MovementDirectionSubmit
User receives cryptoInboundYes
User sends cryptoOutboundYes
Fiat or card activityNo. Card transactions are already visible to Wirex — see Card Transactions
Internal ledger movements with no on-chain transactionNo. The endpoint requires a real transaction hash

Submit after confirmation, not on broadcast. The report is attributed to a specific on-chain
transaction, and an unconfirmed hash that never lands leaves a report referencing a transaction that
does not exist.


Request

{
  "direction": "Outbound",
  "user_address": "0xA7E41d5680dE394EaA2ed417169DFf56840Fb3EE",
  "amount": 99.99,
  "asset_symbol": "USDC",
  "chain_name": "Base",
  "transaction_hash": "0x0000000000000000000000000000000000000000000000000000000000000001",
  "sender_address": "0xA7E41d5680dE394EaA2ed417169DFf56840Fb3EE",
  "recipient_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
}
FieldTypeRequiredDescription
directionstringYesInbound or Outbound. Determines whether the transaction is screened as a deposit or as a transfer
user_addressstringYesWallet address the report is attributed to — the address of your user, not the counterparty
amountnumberYesTransaction amount in the transaction's own asset. Must be greater than 0
asset_symbolstringYesToken or currency symbol, e.g. USDC
chain_namestringYesNetwork the transaction settled on. See Supported Networks
transaction_hashstringYesOn-chain transaction hash
sender_addressstringYesAddress funds moved from
recipient_addressstringYesAddress funds moved to

user_address is a separate field from sender_address and recipient_address on purpose. It is
whose report this is. On an Outbound transaction it usually equals sender_address; on an
Inbound one it usually equals recipient_address — but the endpoint does not infer it, and a wrong
value files the report against the wrong user.

Address Format

user_address, sender_address and recipient_address are all validated against the same
multi-chain pattern:

^(0x[a-fA-F0-9]{40}|C[A-Za-z2-7]{55}|G[A-Za-z2-7]{55}|T[1-9A-HJ-NP-Za-km-z]{33}|r[1-9A-HJ-NP-Za-km-z]{24,34})$
PrefixChain familyFormat
0xEVM40 hex characters
C / GStellar55 base32 characters
TTron33 base58 characters
rXRPL24–34 base58 characters

Bitcoin, Cosmos, Solana and Waves addresses do not match this pattern, even though those networks
appear in chain_name. See Limitations.

Supported Networks

chain_name must be one of the following. Values are case-sensitive and are not the same strings
used elsewhere in the API
— send Base, not BASE; Stellar, not STELLAR.

ArbitrumNativeArbitrumCChainCChainBridged
CChainBridgedBitcoinCChainBridgedBinanceWrappedCChainWrappedCChainBridgedEthereum
BinanceSmartChainBitcoinEthereumEthereumBRZ
WrappedEthereumPolygonPolygonBRZPolygonBridgedBinance
WrappedPolygonCosmosTronWrappedTron
WavesStellarRippleSolana
Base

Anything outside this list is refused with chain_name: {value} and issue: invalid_value.


Response

{}

A 200 means the transaction was accepted into the screening pipeline. It is not a risk verdict.
Screening happens after the call returns, and this endpoint does not tell you whether the transaction
was flagged. Do not gate a user action on the response.


Code Examples

await fetch(`${baseUrl}/api/v1/transactions/risk-assessment`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${accessToken}`,
    'X-User-Wallet': userEoaAddress,
    'X-Chain-Id': chainId,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    direction: 'Outbound',
    user_address: userEoaAddress,
    amount: 99.99,
    asset_symbol: 'USDC',
    chain_name: 'Base',
    transaction_hash: transactionHash,
    sender_address: userEoaAddress,
    recipient_address: recipientAddress
  })
});
requests.post(
    f"{base_url}/api/v1/transactions/risk-assessment",
    headers={
        "Authorization": f"Bearer {access_token}",
        "X-User-Wallet": user_eoa_address,
        "X-Chain-Id": chain_id,
        "Content-Type": "application/json"
    },
    json={
        "direction": "Outbound",
        "user_address": user_eoa_address,
        "amount": 99.99,
        "asset_symbol": "USDC",
        "chain_name": "Base",
        "transaction_hash": transaction_hash,
        "sender_address": user_eoa_address,
        "recipient_address": recipient_address
    }
)
body, _ := json.Marshal(map[string]interface{}{
    "direction":         "Outbound",
    "user_address":      userEoaAddress,
    "amount":            99.99,
    "asset_symbol":      "USDC",
    "chain_name":        "Base",
    "transaction_hash":  transactionHash,
    "sender_address":    userEoaAddress,
    "recipient_address": recipientAddress,
})

req, _ := http.NewRequest("POST", baseURL+"/api/v1/transactions/risk-assessment", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+accessToken)
req.Header.Set("X-User-Wallet", userEoaAddress)
req.Header.Set("X-Chain-Id", chainId)
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

Limitations

  • The response carries no verdict. Acceptance is not clearance, and a flagged transaction does
    not surface here. Screening outcomes are handled through compliance channels, not this endpoint.
  • No deduplication guarantee. Submitting the same transaction_hash twice produces two reports.
    Track what you have already sent rather than replaying a window on every run.
  • No batch endpoint. One call per transaction.
  • Address validation is narrower than the network list. chain_name accepts Bitcoin, Cosmos,
    Solana and Waves, but the address pattern only matches EVM, Stellar, Tron and XRPL formats — a
    Bitcoin transaction cannot currently be submitted with genuine Bitcoin addresses. Raise these with
    Wirex rather than substituting a placeholder address.
  • chain_name casing is its own vocabulary, not the chain names used in the token catalogue or
    the X-Chain-Id header.
  • amount must be positive. Zero-value transactions are refused, so they cannot be reported.

Error Handling

Validation Errors (400)

Error DescriptionfieldissueResolution
direction must be Inbound or Outbounddirectioninvalid_valueSend Inbound or Outbound, exact casing
user address must be a valid addressuser_addressinvalid_addressMatch the address pattern. pattern details carry the regex
Invalid value for amountamountinvalid_valueSend an amount greater than 0
asset symbol is requiredasset_symbolmissingSend the token symbol
chain_name must be a supported networkchain_nameinvalid_valueUse a value from Supported Networks
transaction hash is requiredtransaction_hashmissingSend the on-chain hash
sender address must be a valid addresssender_addressinvalid_addressMatch the address pattern
recipient address must be a valid addressrecipient_addressinvalid_addressMatch the address pattern

Example Error Response

{
  "error_reason": "ErrorInvalidField",
  "error_description": "chain_name must be a supported network",
  "error_category": {
    "category": "CategoryValidationFailure",
    "http_status_code": 400
  },
  "error_details": [
    { "key": "field", "details": "chain_name" },
    { "key": "issue", "details": "invalid_value" },
    { "key": "chain_name", "details": "BASE" }
  ]
}

Missing fields return ErrorMissingField; malformed ones return ErrorInvalidField. See
Error Reference for the full reason and category lists.

Failures After Validation

A 500 means the transaction did not reach the screening pipeline. Retry with backoff and keep the
transaction in your outbox until a call succeeds — a dropped report is a compliance gap, not a
missing log line.


Did this page help you?