# Travel rule API: IVMS 101 data, protocols and what to build

URL: https://fincodelabs.com/en/blog/crypto-travel-rule-api · Publisher: Fincode (Fincode Yazılım ve Teknoloji A.Ş.), Istanbul, Turkey · Category: Fintech development · Published: 2026-11-06 · Updated: 2026-11-06

Travel rule API build guide: who is covered, IVMS 101 fields with a tested validator, protocols, unhosted wallets, message states, retries and storage.

**Short answer:** A **travel rule API** is the interface a crypto asset service provider uses to exchange originator and beneficiary data with another provider around a transfer, and no single official API exists. IVMS 101 defines only the data model, so a build also needs discovery, a protocol, a state machine that holds funds and retention. As read on 6 November 2026, the EU sets no minimum between providers, Turkey adds identifiers at TRY 30,000, the UK at GBP 800 across borders, and the US rule applies from USD 3,000.

This guide is general education, not legal advice, and Fincode is a software company, not a crypto asset service provider. Documents were read on 6 November 2026 and appear alphabetically, with no ranking.

## Travel rule applicability and requirements by jurisdiction

The rule binds the regulated provider moving crypto for a customer; triggers vary by law.

FATF's Interpretive Note to Recommendation 15, quoted in [IVMS 101.2023](https://cdn.prod.website-files.com/648841abc97f28489cc3f2ce/6656e9c60c3029989dcd7431_IVMS101.2023%20interVASP%20data%20model%20standard.pdf) and a [Government of Jersey paper](https://www.gov.je/SiteCollectionDocuments/Government%20and%20administration/Consultation%20regarding%20amendments%20to%20the%20EU%20Legislation.pdf), asks countries to make originating providers submit data "immediately and securely". The EU text excludes person-to-person transfers with no provider, and transfers between providers acting on their own behalf (Art. 2(4)).

**Legal texts read 6 November 2026; not legal advice**

| Place and text | Trigger | Message data | Self-hosted or unhosted |
| --- | --- | --- | --- |
| [EU: Regulation 2023/1113](https://eur-lex.europa.eu/eli/reg/2023/1113/oj), from 30 December 2024 | No minimum (Art. 14) | Originator: name, ledger address and account, address with country, document and customer numbers or birth details, legal entity identifier (LEI) if a field exists. Beneficiary: name, ledger address and account | Obtain and hold data; above EUR 1,000 assess control (Art. 14(5), 16(2)) |
| [Turkey: Measures Regulation, Art. 24/A](https://www.mevzuat.gov.tr/MevzuatMetin/21.5.200713012.pdf) | Full set at TRY 30,000 or more, since 7 October 2026 | Full set: sender name, wallet or reference, one identifier (address, birth details, customer, citizenship, passport or tax number), verified; recipient name and wallet. Below: names and wallets, unverified | Customer declaration; risk-based extra documents; consider not executing (Art. 24/A(6), (8)) |
| [UK: Money Laundering Regulations 2017, Part 7A](https://www.legislation.gov.uk/uksi/2017/692/part/7A) | Names and accounts always; identifier at GBP 800 or more with linked transfers, since 30 June 2026 ([reg. 64C(4)](https://www.legislation.gov.uk/uksi/2017/692/regulation/64C)); in UK-only chains, on request within three working days (64C(2)) | Names; account numbers or transaction identifier; one originator identifier | May request from the customer; must not release if requested data does not arrive (reg. 64G) |
| [US: 31 CFR 1010.410(f)](https://www.govinfo.gov/content/pkg/CFR-2025-title31-vol3/pdf/CFR-2025-title31-vol3-sec1010-410.pdf), [FinCEN FIN-2019-G001](https://www.fincen.gov/sites/default/files/2019-05/FinCEN%20Guidance%20CVC%20FINAL%20508.pdf) | USD 3,000 or more | Transmittor name, account, address; amount; date; recipient's institution; recipient data as received | Ask counsel |

Amounts define duties, not design targets: the UK counts linked transfers, and no build should split or shape transfers around a figure. Turkey's figure doubled from TRY 15,000 under Presidential Decision 11845 (Resmî Gazete 33393); the [September 2025 guide](https://ms.hmb.gov.tr/uploads/sites/12/2025/09/KVHS-Yukumluluklere-Uyum-Rehberi-740beebec7fb3391.pdf) from MASAK, Turkey's financial intelligence unit, still shows TRY 15,000. The regulation does not say how to value crypto in lira. Article 99/A of Capital Markets Law No. 6362 treats activity by foreign crypto-asset platforms directed at Turkish residents as unauthorized. See [is crypto legal in Turkey](https://fincodelabs.com/en/blog/is-crypto-legal-in-turkey) and [Turkish crypto exchange APIs](https://fincodelabs.com/en/blog/turkish-crypto-exchange-apis).

## What data moves: IVMS 101 and what it leaves out

IVMS 101.2023 standardizes who the parties are and says nothing about how the message travels.

- **Out of scope.** Section 3 excludes information security, privacy, identifying providers, network protocols and storage.
- **ASCII names.** `primaryIdentifier` allows printable ASCII only, so a name such as Öztürk needs a Latin form there and the original in `localNameIdentifier`. The transliteration table does not cover Turkish letters, so fix a rule and expect name-matching differences.
- **No JSON binding.** The PDF never mentions JSON, and names vary: its dictionary says `naturalPersonNameIdentifierType`, its example says `nameIdentifierType`, and [GTR's 2020-edition page](https://www.globaltravelrule.com/documentation/ivms-101-guidelines) nests `originatorPersons` under a capitalized `Originator` key. [TRISA](https://trisa.dev/data/ivms/index.html) warns that its protojson output fails a third-party validator. Normalize at the boundary and keep the raw payload.

## A tested IVMS 101 validator in Python

A validator catches malformed payloads before a counterparty does, but a clean result does not mean the message meets your law.

This ran on synthetic data on 6 November 2026 (Python 3.11.3, standard library) and covers parts of constraints C1, C6 and C11 only. No live protocol or vendor API was called.

```python
import copy, math, re

def filled(v):  # blanks are not data
    if isinstance(v, (dict, list)):
        return any(map(filled, v.values() if isinstance(v, dict) else v))
    return isinstance(v, str) and bool(v.strip())

def has_id(p):  # C1: codes alone do not identify
    nat, birth = p.get("nationalIdentification") or {}, p.get("dateAndPlaceOfBirth") or {}
    places = [{k: v for k, v in a.items() if k not in ("addressType", "country")}
              for a in p.get("geographicAddress") or []]
    return (filled([p.get("customerIdentification"), nat.get("nationalIdentifier"), places])
            or filled(birth.get("dateOfBirth")) and filled(birth.get("placeOfBirth")))

def lei_ok(lei):  # pattern, then ISO 7064 check digits
    return isinstance(lei, str) and bool(re.fullmatch("[0-9A-Z]{18}[0-9]{2}", lei)) \
        and int("".join(str(int(c, 36)) for c in lei)) % 97 == 1

def persons(payload, side):  # 2020-era payloads use plural keys
    block = payload.get(side) or {}
    return (block.get(side + "Person") or []) + (block.get(side + "Persons") or [])

def person_errors(person, is_originator):
    natural, legal = person.get("naturalPerson"), person.get("legalPerson")
    p = natural or legal
    if not p or natural and legal:
        return ["exactly one of naturalPerson, legalPerson"]
    errors, names = [], (p.get("name") or {}).get("nameIdentifier") or []
    if natural:
        if not any(n.get("naturalPersonNameIdentifierType",
                         n.get("nameIdentifierType")) == "LEGL" for n in names):
            errors.append("C6 no legal name")
        if not all(re.fullmatch("[ -~]{1,100}", str(n.get("primaryIdentifier") or ""))
                   for n in names):
            errors.append("Max100Text: use localNameIdentifier")
        if is_originator and not has_id(p):
            errors.append("C1 no identifying data")
    else:
        national = p.get("nationalIdentification") or {}
        if national.get("nationalIdentifierType") == "LEIX" and \
                not lei_ok(national.get("nationalIdentifier")):
            errors.append("C11 bad LEI")
    return errors

def check(payload):
    errors = []
    for side in ("originator", "beneficiary"):
        for person in persons(payload, side) or [{}]:
            errors += person_errors(person, side == "originator")
    return sorted(set(errors))

def turkey_24a_gaps(payload, amount_try):
    """Art. 24/A(1)-(2); verification is separate."""
    if type(amount_try) not in (int, float) or not math.isfinite(amount_try) or amount_try < 0:
        raise ValueError(f"bad amount: {amount_try}")
    gaps = [f"{side} wallet" for side in ("originator", "beneficiary")
            if not filled((payload.get(side) or {}).get("accountNumber"))]
    for person in persons(payload, "originator") or [{}]:
        p = person.get("naturalPerson") or person.get("legalPerson") or {}
        if amount_try >= 30_000 and not has_id(p):
            gaps.append("one sender identifier")
    return sorted(set(gaps))

good = {
 "payloadMetadata": {"payloadVersion": "101.2023"},
 "originator": {"accountNumber": ["addr-1"], "originatorPerson": [{"naturalPerson": {
   "name": {"nameIdentifier": [{"primaryIdentifier": "Example",
                                "naturalPersonNameIdentifierType": "LEGL"}]},
   "customerIdentification": "C-1001"}}]},
 "beneficiary": {"accountNumber": ["addr-2"], "beneficiaryPerson": [{"legalPerson": {
   "name": {"nameIdentifier": [{"legalPersonName": "Example Ltd",
                                "legalPersonNameIdentifierType": "LEGL"}]},
   "nationalIdentification": {"nationalIdentifier": "SYNTHETICTEST0000031",
                              "nationalIdentifierType": "LEIX"}}}]}}
print("valid:", check(good))

bad = copy.deepcopy(good)
person = bad["originator"]["originatorPerson"][0]["naturalPerson"]
person["customerIdentification"] = " "
person["name"]["nameIdentifier"][0].update(primaryIdentifier="Öztürk",
                                           naturalPersonNameIdentifierType="ALIA")
legal = bad["beneficiary"]["beneficiaryPerson"][0]["legalPerson"]
legal["nationalIdentification"]["nationalIdentifier"] = "3M5E1GQKGL17HI8CPN20"
print("broken:", check(bad))
for amount in (10_000, 30_000, float("nan")):
    try:
        print(f"Art. 24/A at TRY {amount:,}:", turkey_24a_gaps(bad, amount))
    except ValueError as e:
        print("rejected:", e)
```

Output:

```
valid: []
broken: ['C1 no identifying data', 'C11 bad LEI', 'C6 no legal name', 'Max100Text: use localNameIdentifier']
Art. 24/A at TRY 10,000: []
Art. 24/A at TRY 30,000: ['one sender identifier']
rejected: bad amount: nan
```

- **Turkey versus IVMS.** At TRY 10,000 the Art. 24/A check passes while C1 fails: Turkey wants only names and wallets below the limit; a strict IVMS receiver wants an identifier.
- **Silent passes.** The standard's example LEI fits its own pattern but fails the check digits, and NaN fails every comparison, so an unguarded amount check passes it as small.
- **EU versus IVMS.** C1 accepts a customer number alone. Art. 14(1)(d) lists more, and [European Banking Authority (EBA) Guidelines](https://www.eba.europa.eu/sites/default/files/2024-07/6de6e9b9-0ed9-49cd-985d-c0834b5b4356/Travel%20Rule%20Guidelines.pdf) paragraph 39 asks for the combination that best identifies the person. Have compliance write that rule.

## Counterparty discovery and travel rule protocols

Each route finds counterparties its own way, and the EBA warns that providers might need several systems because not all interoperate.

**Alphabetical, not a ranking; none was run**

| Route | What its documents describe | Discovery and security |
| --- | --- | --- |
| [Binance](https://developers.binance.com/en/docs/catalog/core-trading-wallet/api/rest-api/travel-rule) (platform API) | `POST /sapi/v1/localentity/withdraw/apply` and deposit questionnaires for local entities; country list "Currently supports AU and BR entities." | For its own users, not a network; Turkey not mentioned |
| [GTR](https://www.globaltravelrule.com/documentation) | API with callback URL | Provider allowlist; mTLS; end-to-end encryption required |
| [Notabene](https://devx.notabene.id/v1.0/docs/sending) (vendor API) | `txCreate`, `txValidateFull`, `txNotify` | Counterparty DID from its widget or `txValidateFull` |
| [Sumsub](https://docs.sumsub.com/docs/travel-rule-overview) (vendor API) | API, SDK, or Fireblocks or DFNS setups, over GTR, CODE, Sygna Bridge, TRP and its own protocol; sandbox test provider | Wallet Address Book, protocol lookups, blockchain analytics |
| [TRP](https://gitlab.com/OpenVASP/travel-rule-protocol/-/blob/master/core/specification.md) | HTTPS and JSON; specification 3.2.1, 29 July 2024 | No directory: a Travel Address from the beneficiary's provider; mTLS; payload encryption only through extensions, [per TRISA](https://trisa.dev/openvasp/index.html) |

The texts name no protocol: EBA paragraph 25 and Art. 24/A(5) in Turkey both allow APIs and independent messaging. EBA paragraph 26 lists the tests: connectivity, reachability, missing-data detection, and data integration, security and reliability. TRISA documents a partial bridge to TRP. The domain travelruleprotocol.org now serves unrelated content, so take TRP from OpenVASP's GitLab.

## Unhosted wallets and self-hosted addresses

You collect the data from your own customer and, in the EU above EUR 1,000, assess who controls the address.

- **Detect.** EBA paragraphs 77 and 78 point to blockchain analytics, data providers and messaging identifiers, then the customer.
- **Limit.** Art. 14(5) says "exceeding EUR 1 000" and EBA paragraph 81 says "amounts to or exceeds", so settle exactly EUR 1,000 with compliance. Value the transfer at its exchange rate at that time, regardless of fees (paragraph 82).
- **Prove.** Paragraph 83 lists attended or unattended verification, a small transfer from and to the address, and a signed message. Paragraph 86 allows whitelisting with monitoring.

> **Hypothetical EU example.** A EUR 1,200 withdrawal to a self-hosted address: hold the originator and beneficiary data and assess control before it runs. A EUR 300 withdrawal needs the data only.

## Outbound and inbound states: hold funds on the message

Keep message state separate from chain state, and let the message state decide when funds may move.

**Mapping to two documented flows**

| Your state | Meaning | TRP | [Notabene](https://devx.notabene.id/v1.0/docs/tr-status_vnext) |
| --- | --- | --- | --- |
| `SENT` | Awaiting counterparty | Inquiry POST | `SENT` |
| `APPROVED` | May broadcast | `approved` | `ACCEPTED` |
| `REFUSED` | Do not broadcast | `rejected` | `REJECTED`, `DECLINED` |
| `BROADCAST` | Hash shared | `txid` | `txhash` added |

- **Outbound.** EU Art. 14(8) bars executing before compliance, and EBA paragraph 25 says data goes no later than initiation of the blockchain transaction. A timeout goes to review, never to release.
- **Inbound.** A deposit can land before its data, so hold it (Art. 16(3), 17(1)): Notabene's `txNotify` then opens an `INCOMPLETE` message, and Binance's `travelRuleStatusV2` field defines `PENDING` as frozen. Ask through the same channel (paragraph 58).
- **Deadlines.** EBA paragraph 56 caps a request at three working days within the Union and five from outside, or up to seven for longer or non-EU chains. Then reject or return (Art. 17(1)); paragraph 55 points to a secure, segregated account when a return is impossible. In Turkey, ask and then return (Art. 24/A(3)).

## Retries, idempotency and repeat failures

Documents differ on retries, so put each route behind one internal transfer ID and its own policy.

- [GTR](https://www.globaltravelrule.com/documentation/error-handling): requests run once, except three HTTP 500 errors, which it suggests retrying with the same `requestId` every 30 seconds, up to 5 times.
- Notabene: a repeated `transactionRef` returns 409 while the first call is processing and 200 with the existing message afterward.
- TRP: a new UUID per call, retries left undefined, and approval shown both as an immediate reply and as a callback.

Use an outbox, deduplicate inbound messages on a non-empty message ID and count failures per counterparty: EU Art. 17(2) requires action and reporting on repeat failures, UK reg. 64D(5) reporting to the FCA, and Turkey's Art. 24/A(3) points to rejecting transfers or ending the relationship.

## Storage, privacy and audit

Keep what proves each decision as long as the law requires, and no longer.

- **Retention.** EU Art. 26 sets five years, then deletion unless national law allows more. Turkey sets eight years (Measures Regulation Art. 46).
- **Record.** Raw and normalized payload, protocol, counterparty, timestamps, checks, decision and person. Turkish platforms also collect a transfer description of at least 20 characters ([MASAK General Communiqué 29, Art. 4(5)](https://resmigazete.gov.tr/eskiler/2025/06/20250628-4.htm)).
- **Privacy.** EU Art. 14(4) ties submission to security and the GDPR. Keep personal data in a separate encrypted store and only references in logs.

Transaction screening is covered in crypto payment API integration. For integration work in general, see Fincode's [exchange API integration](https://fincodelabs.com/en/services/exchange-api-integration) and [Turkish exchange connectors](https://fincodelabs.com/en/services/turkish-exchange-connectors) pages; naming Binance or a vendor here does not mean Fincode integrates it or has built on it. Licensing and compliance decisions stay with the regulated provider.

## Frequently asked questions

### Does the travel rule cover person-to-person transfers?

Not in the EU text when no provider is involved: Article 2(4) excludes them.

### What is the travel rule threshold in Turkey?

TRY 30,000 since 7 October 2026 for the full data set, with the sender's details verified. Below it, names and wallet addresses still travel.

### What do the Sumsub, Notabene and Binance travel rule APIs do?

Sumsub and Notabene document vendor layers for exchanging data with other providers. Binance documents endpoints for its own local entities.

### Is IVMS 101 a JSON schema?

No. IVMS 101.2023 is a data model with no JSON binding, so field names vary between implementations.

## Sources

- [Regulation (EU) 2023/1113 on information accompanying transfers of funds and certain crypto-assets](https://eur-lex.europa.eu/eli/reg/2023/1113/oj)
- [EBA Travel Rule Guidelines, EBA/GL/2024/11](https://www.eba.europa.eu/sites/default/files/2024-07/6de6e9b9-0ed9-49cd-985d-c0834b5b4356/Travel%20Rule%20Guidelines.pdf)
- [interVASP: IVMS 101.2023 data model standard](https://cdn.prod.website-files.com/648841abc97f28489cc3f2ce/6656e9c60c3029989dcd7431_IVMS101.2023%20interVASP%20data%20model%20standard.pdf)
- [Mevzuat: Measures Regulation, Articles 24/A and 46, consolidated (Turkish)](https://www.mevzuat.gov.tr/MevzuatMetin/21.5.200713012.pdf)
- [Mevzuat: Capital Markets Law No. 6362, Article 99/A (Turkish)](https://www.mevzuat.gov.tr/mevzuatmetin/1.5.6362.pdf)
- [UK Money Laundering Regulations 2017, Part 7A](https://www.legislation.gov.uk/uksi/2017/692/part/7A)
- [US 31 CFR 1010.410, July 2025 edition](https://www.govinfo.gov/content/pkg/CFR-2025-title31-vol3/pdf/CFR-2025-title31-vol3-sec1010-410.pdf)
- [FinCEN Guidance FIN-2019-G001, 9 May 2019](https://www.fincen.gov/sites/default/files/2019-05/FinCEN%20Guidance%20CVC%20FINAL%20508.pdf)
- [OpenVASP: TRP core specification 3.2.1](https://gitlab.com/OpenVASP/travel-rule-protocol/-/blob/master/core/specification.md)
- [TRISA: OpenVASP and TRP integration](https://trisa.dev/openvasp/index.html)
- [GTR: error handling and retries](https://www.globaltravelrule.com/documentation/error-handling)
- [Notabene: Travel Rule statuses](https://devx.notabene.id/v1.0/docs/tr-status_vnext)
- [Sumsub: Travel Rule overview](https://docs.sumsub.com/docs/travel-rule-overview)
- [Binance: Travel Rule wallet REST API](https://developers.binance.com/en/docs/catalog/core-trading-wallet/api/rest-api/travel-rule)
- [MASAK: compliance guide for crypto asset service providers, September 2025 (Turkish)](https://ms.hmb.gov.tr/uploads/sites/12/2025/09/KVHS-Yukumluluklere-Uyum-Rehberi-740beebec7fb3391.pdf)

> This guide is general information about software and markets, not investment, legal or tax advice. Automated trading can lose money, past or backtested performance does not guarantee future results, and the rules that apply depend on your jurisdiction and your exchange's terms. Sources, checks and corrections: see the [editorial policy](https://fincodelabs.com/en/editorial-policy).

---

Published by Fincode (Fincode Yazılım ve Teknoloji A.Ş.), a software company in Istanbul, Turkey: https://fincodelabs.com/en/blog/crypto-travel-rule-api

Cite as: Fincode, "Travel rule API: IVMS 101 data, protocols and what to build", updated November 6, 2026, https://fincodelabs.com/en/blog/crypto-travel-rule-api
