# Send assets with PII

This workflow covers outgoing transfers where PII (Personally Identifiable Information) is required. You collect PII from your end user and submit it via the API before creating the transfer intent.

## Prerequisites

- [Travel Rule setup](/products/custody/v1.36/compliance/travel-rule/setup) complete.
- Wallet addresses registered with Notabene.


## Process flow


```mermaid
sequenceDiagram
    participant Customer
    participant EndUser as End User
    participant Custody
    participant Notabene
    participant Blockchain

    Customer->>Custody: 1. Create Travel Rule message
    Custody->>Notabene: Forward to Notabene
    Notabene-->>Custody: Return transfer details
    Custody-->>Customer: Return suggestedIntentId, travelRuleId

    Customer->>Custody: 2. Get transfer status (check PII requirements)
    Custody-->>Customer: Return presentation-definition with policyId

    Customer->>EndUser: 3. Request PII
    EndUser-->>Customer: Provide PII

    Customer->>Custody: 4. Submit PII via API
    Custody->>Notabene: Forward PII
    Notabene-->>Custody: PII attached
    Custody-->>Customer: Confirmed

    Customer->>Custody: 5. Create Transfer Order Intent (using suggestedIntentId)

    Custody->>Custody: 6. Screen risk (if configured)
    alt Screening failed
        Custody->>Custody: AUTO_REJECTED (no Travel Rule check)
    else Screening passed or not configured
        Custody->>Notabene: 7. Travel Rule check
    end

    Custody->>Custody: 8. Compliance decision

    alt AUTO_APPROVED
        Custody->>Blockchain: 9. Execute transaction
        Custody->>Notabene: Notify settlement
    else AUTO_REJECTED
        Custody->>Notabene: Notify rejection
    end
```

## Step 1: Create Travel Rule message

Create a Travel Rule message with counterparty information:


```bash
POST /v1/domains/{domainId}/compliance/travel-rule/providers/{provider}/messages
```

**Request body**:

| Field | Type | Description |
|  --- | --- | --- |
| `originator` | object | Originator identifier (contains `@id` - DID or identifier for the party) |
| `beneficiary` | object | Beneficiary identifier (contains `@id` - DID or identifier for the party) |
| `asset` | string | Asset identifier (e.g., `bip122:000000000019d6689c085ae165831e93/slip44:0`). See [Notabene asset registry](https://devx.notabene.id/docs/supported-assets) for supported formats. |
| `amount` | string | Transfer amount |
| `ref` | string | Reference identifier for the transfer |
| `agents` | array | Agents involved in the transfer. Each agent has `@id`, `for`, and `role` (values: `VASP`, `Custodian`, `SettlementAddress`, `SourceAddress`, `Gateway`, `Unknown`) |


**Response**:

| Field | Type | Description |
|  --- | --- | --- |
| `createTransfer201Response.transfer` | object | Transfer details from Notabene |
| `suggestedIntentId` | string (uuid) | The intent ID to use when creating the transfer intent |


## Step 2: Check PII requirements

Get the transfer status to check what PII is required:


```bash
GET /v1/domains/{domainId}/compliance/travel-rule/providers/{provider}/messages/{travelRuleId}
```

The response includes a `presentation-definition` that specifies the required PII fields. It also contains a `policyId` that you can use when submitting encrypted PII (see Step 4, Option C).

## Step 3: Collect PII from end user

Collect required PII from your end user according to the IVMS-101 standard and the requirements in the `presentation-definition`.

## Step 4: Submit PII via API

Submit the collected PII using one of three API endpoints. All PII is encrypted with the receiver's public key.

### Option A: Append PII (stored on both sides)

Use this option when both sender and receiver need access to the PII.


```bash
POST /v1/domains/{domainId}/compliance/travel-rule/providers/{provider}/messages/{travelRuleId}/pii
```

**Request body**:

| Field | Type | Required | Description |
|  --- | --- | --- | --- |
| `ivms101` | object | Yes | IVMS-101 formatted PII data |
| `originator` | object | No | Originator details |
| `beneficiary` | object | No | Beneficiary details |


**Response:** `200 OK`

### Option B: Present encrypted PII (stored on receiver side only)

Use this option for end-to-end encrypted PII that only the receiver can decrypt.


```bash
POST /v1/domains/{domainId}/compliance/travel-rule/providers/{provider}/messages/{travelRuleId}/encrypted-pii
```

**Request body**:

| Field | Type | Required | Description |
|  --- | --- | --- | --- |
| `ivms101` | object | Yes | End-to-end encrypted IVMS-101 data |


**Response:** `202 Accepted` (asynchronous processing)

### Option C: Present encrypted PII with policy (stored on receiver side only)

Use this option when sender and receiver have different jurisdictional requirements. The `policyId` from Step 2 specifies the applicable compliance policy.


```bash
POST /v1/domains/{domainId}/compliance/travel-rule/providers/{provider}/messages/{travelRuleId}/policies/{travelRulePolicyId}/encrypted-pii
```

**Request body:** Same as Option B.

**Response:** `202 Accepted` (asynchronous processing)

Options B and C use end-to-end encryption with the receiver's public key. The sender cannot decrypt this data after submission.

## Step 5: Create Transfer Order intent

Create the Transfer Order intent using the `suggestedIntentId` returned in Step 1.

**Transfer Order intent payload**:

| Field | Type | Required | Description |
|  --- | --- | --- | --- |
| `payload.accountId` | string (uuid) | Yes | Source account ID |
| `payload.ledgerId` | string | No | Ledger identifier |
| `payload.parameters.operation` | object | Yes | Transfer operation details including `destination`, `amount`, and `type` |
| `payload.type` | string | Yes | Must be `v0_CreateTransferOrder` |


Critical
Use the `suggestedIntentId` from Step 1 when creating the intent. This links the compliance check to your transaction.

## Step 6: Risk screening

If transaction screening is configured (Chainalysis or Elliptic), Ripple Custody screens the transaction first.

- **If screening fails** (high risk score): The transaction is immediately rejected. No Travel Rule check occurs.
- **If screening passes**: The workflow proceeds to the Travel Rule check.


## Step 7: Travel Rule check

Notabene verifies counterparty information and PII.

## Step 8: Compliance decision

Ripple Custody evaluates the results:

| Screening Result | Travel Rule Result | Decision |
|  --- | --- | --- |
| Approved | Approved | `AUTO_APPROVED` |
| Rejected | — | `AUTO_REJECTED` (Travel Rule skipped) |
| Approved | Rejected | `AUTO_REJECTED` |
| Inconclusive | Inconclusive | `NEED_EXPLICIT_DECISION` |


If the decision is `NEED_EXPLICIT_DECISION`, manual review is required in Ripple Custody.

If only one check is configured (screening only or Travel Rule only), the decision is based on that single check.

## Step 9: Execution and settlement

- If `AUTO_APPROVED`: Transaction executes on blockchain, then Notabene is notified of settlement.
- If `AUTO_REJECTED`: Intent closes, then Notabene is notified of rejection.


## Verify transfer status

Poll the Travel Rule transfer status to confirm successful execution:


```bash
GET /v1/domains/{domainId}/compliance/travel-rule/providers/{provider}/messages/{travelRuleId}
```

**Key response fields**:

| Field | Type | Description |
|  --- | --- | --- |
| `transfer.status` | string | Notabene transfer status (e.g., `SETTLED`, `REJECTED`) |
| `transfer.direction` | string | `OUTGOING` for this workflow |


## Next steps

- [Send without PII](/products/custody/v1.36/compliance/travel-rule/outgoing-no-pii) — When PII is not required
- [Receive assets](/products/custody/v1.36/compliance/travel-rule/incoming) — Handle incoming Travel Rule transfers