Deposits, funds first

Sometimes the funds reach your customer’s address before any Travel Rule data. They may come from an institution that did not send it, or from a wallet with no institution behind it. In that case you create the transfer yourself from the deposit, and either ask the sending institution for its data or record the sender your customer tells you about.

Before you start

  • An access token, as described in Authentication. The examples use the sandbox base URL.
  • Your customer and the address the funds landed on, registered with Notabene, as described in Wallets.
  • A webhook endpoint, as described in Webhooks, to learn when the transfer is settled.

1. Look for a transfer

First check that no transfer explains the deposit, as described in Deposits. The sender records the transaction hash only once it sees its withdrawal complete, so retry the lookup with a back-off and stop after a retry window you define.

API reference
curl --get https://sandbox.notabene.id/api/v1/transfers/match \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --data-urlencode 'settlement_id=eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d' \
  --data-urlencode 'settlement_address=eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed'const url = new URL('https://sandbox.notabene.id/api/v1/transfers/match');
url.searchParams.set('settlement_id', 'eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d');
url.searchParams.set('settlement_address', 'eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed');

const response = await fetch(url, {
  method: 'GET',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
  },
});
const answer = response.status === 204 ? null : await response.json();import requests

response = requests.get(
    'https://sandbox.notabene.id/api/v1/transfers/match',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    params={
      "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
      "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
    },
)
answer = None if response.status_code == 204 else response.json()
What to replace
  • eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d: The deposit’s transaction hash, as CAIP-220 or a bare hash on the asset’s chain
  • eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed: Your customer’s address the funds landed on
Nothing yet200
{
  "data": [],
  "all_filters_applied": true,
  "pagination": {
    "total": 0,
    "limit": 25
  }
}
Found200
{
  "data": [
    {
      "id": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
      "type": "Transfer",
      "status": "Settled",
      "direction": "Incoming",
      "counterparty": "did:web:originator.example:pt",
      "asset": "eip155:1/slip44:60",
      "amount": "0.5"
    }
  ],
  "all_filters_applied": true,
  "pagination": {
    "total": 1,
    "limit": 25
  }
}
curl --get https://sandbox.notabene.id/api/v1/transfers/match \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --data-urlencode 'settlement_id=eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d' \
  --data-urlencode 'settlement_address=eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed'
const url = new URL('https://sandbox.notabene.id/api/v1/transfers/match');
url.searchParams.set('settlement_id', 'eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d');
url.searchParams.set('settlement_address', 'eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed');

const response = await fetch(url, {
  method: 'GET',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
  },
});
const answer = response.status === 204 ? null : await response.json();
import requests

response = requests.get(
    'https://sandbox.notabene.id/api/v1/transfers/match',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    params={
      "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
      "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed"
    },
)
answer = None if response.status_code == 204 else response.json()
{
  "data": [],
  "all_filters_applied": true,
  "pagination": {
    "total": 0,
    "limit": 25
  }
}
{
  "data": [
    {
      "id": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
      "type": "Transfer",
      "status": "Settled",
      "direction": "Incoming",
      "counterparty": "did:web:originator.example:pt",
      "asset": "eip155:1/slip44:60",
      "amount": "0.5"
    }
  ],
  "all_filters_applied": true,
  "pagination": {
    "total": 1,
    "limit": 25
  }
}

If a transfer matches, the Travel Rule data came first, as described in Deposits, Travel Rule data first.

2. Find out who sent it

Ask Notabene which institution holds the address the funds came from.

API reference
curl https://sandbox.notabene.id/api/v1/wallet-ownerships/discover \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"
  }'const response = await fetch('https://sandbox.notabene.id/api/v1/wallet-ownerships/discover', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"
  }),
});
const answer = response.status === 204 ? null : await response.json();import requests

response = requests.post(
    'https://sandbox.notabene.id/api/v1/wallet-ownerships/discover',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    json={
      "address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"
    },
)
answer = None if response.status_code == 204 else response.json()
What to replace
  • eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359: The address the funds came from
Found200
{
  "source": "AddressHash",
  "owner": "did:web:originator.example:pt",
  "custodian": "did:web:originator.example:pt"
}
Not found404
{
  "title": "Not Found",
  "status": 404,
  "detail": "address owner not found"
}
curl https://sandbox.notabene.id/api/v1/wallet-ownerships/discover \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"
  }'
const response = await fetch('https://sandbox.notabene.id/api/v1/wallet-ownerships/discover', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"
  }),
});
const answer = response.status === 204 ? null : await response.json();
import requests

response = requests.post(
    'https://sandbox.notabene.id/api/v1/wallet-ownerships/discover',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    json={
      "address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359"
    },
)
answer = None if response.status_code == 204 else response.json()
{
  "source": "AddressHash",
  "owner": "did:web:originator.example:pt",
  "custodian": "did:web:originator.example:pt"
}
{
  "title": "Not Found",
  "status": 404,
  "detail": "address owner not found"
}

Keep the owner: it is the institution you ask in step 3. An institution is found only when it registered the address with Notabene, so when nothing is found, ask your customer who sent the funds.

3. Create the transfer

Create the transfer as the beneficiary, with the deposit’s transaction hash. Name the sending institution when step 2 found one, or otherwise name your own DID as the beneficiary’s institution and add the sender in step 4.

curl https://sandbox.notabene.id/api/v1/transfers \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "ref": "0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40",
    "asset": "ETH",
    "amount": "0.5",
    "source_address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
    "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
    "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
    "originator_entity_did": "did:web:originator.example:pt",
    "beneficiary_id": "urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27"
  }'const response = await fetch('https://sandbox.notabene.id/api/v1/transfers', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "ref": "0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40",
    "asset": "ETH",
    "amount": "0.5",
    "source_address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
    "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
    "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
    "originator_entity_did": "did:web:originator.example:pt",
    "beneficiary_id": "urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27"
  }),
});
const answer = response.status === 204 ? null : await response.json();import requests

response = requests.post(
    'https://sandbox.notabene.id/api/v1/transfers',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    json={
      "ref": "0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40",
      "asset": "ETH",
      "amount": "0.5",
      "source_address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
      "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
      "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
      "originator_entity_did": "did:web:originator.example:pt",
      "beneficiary_id": "urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27"
    },
)
answer = None if response.status_code == 204 else response.json()
curl https://sandbox.notabene.id/api/v1/transfers \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "ref": "0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40",
    "asset": "ETH",
    "amount": "0.5",
    "source_address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
    "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
    "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
    "beneficiary_entity_did": "did:web:beneficiary.example:pt",
    "beneficiary_id": "urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27"
  }'const response = await fetch('https://sandbox.notabene.id/api/v1/transfers', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "ref": "0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40",
    "asset": "ETH",
    "amount": "0.5",
    "source_address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
    "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
    "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
    "beneficiary_entity_did": "did:web:beneficiary.example:pt",
    "beneficiary_id": "urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27"
  }),
});
const answer = response.status === 204 ? null : await response.json();import requests

response = requests.post(
    'https://sandbox.notabene.id/api/v1/transfers',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    json={
      "ref": "0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40",
      "asset": "ETH",
      "amount": "0.5",
      "source_address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
      "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
      "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
      "beneficiary_entity_did": "did:web:beneficiary.example:pt",
      "beneficiary_id": "urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27"
    },
)
answer = None if response.status_code == 204 else response.json()
What to replace
  • 0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40: A new UUID of yours for each transfer
  • eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359: The address the funds came from
  • eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed: Your customer’s address the funds landed on
  • eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d: The deposit’s transaction hash, as CAIP-220 or a bare hash on the asset’s chain
  • did:web:originator.example:pt: The owner that step 2 found
  • urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27: The id of your customer who received the funds
  • did:web:beneficiary.example:pt: Your own DID
Response201
{
  "transferId": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
  "status": "Active"
}
curl https://sandbox.notabene.id/api/v1/transfers \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "ref": "0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40",
    "asset": "ETH",
    "amount": "0.5",
    "source_address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
    "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
    "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
    "originator_entity_did": "did:web:originator.example:pt",
    "beneficiary_id": "urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27"
  }'
const response = await fetch('https://sandbox.notabene.id/api/v1/transfers', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "ref": "0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40",
    "asset": "ETH",
    "amount": "0.5",
    "source_address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
    "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
    "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
    "originator_entity_did": "did:web:originator.example:pt",
    "beneficiary_id": "urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27"
  }),
});
const answer = response.status === 204 ? null : await response.json();
import requests

response = requests.post(
    'https://sandbox.notabene.id/api/v1/transfers',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    json={
      "ref": "0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40",
      "asset": "ETH",
      "amount": "0.5",
      "source_address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
      "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
      "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
      "originator_entity_did": "did:web:originator.example:pt",
      "beneficiary_id": "urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27"
    },
)
answer = None if response.status_code == 204 else response.json()
curl https://sandbox.notabene.id/api/v1/transfers \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "ref": "0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40",
    "asset": "ETH",
    "amount": "0.5",
    "source_address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
    "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
    "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
    "beneficiary_entity_did": "did:web:beneficiary.example:pt",
    "beneficiary_id": "urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27"
  }'
const response = await fetch('https://sandbox.notabene.id/api/v1/transfers', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "ref": "0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40",
    "asset": "ETH",
    "amount": "0.5",
    "source_address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
    "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
    "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
    "beneficiary_entity_did": "did:web:beneficiary.example:pt",
    "beneficiary_id": "urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27"
  }),
});
const answer = response.status === 204 ? null : await response.json();
import requests

response = requests.post(
    'https://sandbox.notabene.id/api/v1/transfers',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    json={
      "ref": "0b6a4f8e-2d1c-4e7a-9b3f-5c8d2e1a7f40",
      "asset": "ETH",
      "amount": "0.5",
      "source_address": "eip155:1:0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359",
      "settlement_address": "eip155:1:0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed",
      "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
      "beneficiary_entity_did": "did:web:beneficiary.example:pt",
      "beneficiary_id": "urn:uuid:3c9a7e52-41f6-4b8d-a0e2-6d1f9b4c8a27"
    },
)
answer = None if response.status_code == 204 else response.json()
{
  "transferId": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
  "status": "Active"
}

Keep the transferId: the next calls take it in their path. When you name an institution, Notabene sends it a request for the sender’s data once your policies approve the transfer.

If the institution does not answer, the requirement to receive its data is skipped after 1 minute with either requirements preset, and the transfer goes on without the data. You can still add the sender your customer declares, as in step 4.

FieldWhat to send
ref Required string, UUIDYour own reference for the transfer. Notabene never creates two transfers with the same ref, so a retry is safe.
asset Required stringA symbol such as ETH, or a CAIP-19 identifier.
amount Required string, decimalThe amount received, in the asset’s whole units.
settlement_id Required stringThe deposit’s transaction hash, as CAIP-220 or a bare hash on the asset’s chain.
settlement_address Required stringYour customer’s address the funds landed on.
source_address stringThe address the funds came from.
originator_entity_did string, DIDThe sending institution, when step 2 found one.
beneficiary_entity_did string, DIDYour own DID, when no institution was found. Without one of these two DIDs, the request answers 400.
beneficiary_id Required stringThe id of your customer who received the funds.
FieldWhat to send
ref (required), string, UUIDYour own reference for the transfer. Notabene never creates two transfers with the same ref, so a retry is safe.
asset (required), stringA symbol such as ETH, or a CAIP-19 identifier.
amount (required), string, decimalThe amount received, in the asset’s whole units.
settlement_id (required), stringThe deposit’s transaction hash, as CAIP-220 or a bare hash on the asset’s chain.
settlement_address (required), stringYour customer’s address the funds landed on.
source_address, stringThe address the funds came from.
originator_entity_did, string, DIDThe sending institution, when step 2 found one.
beneficiary_entity_did, string, DIDYour own DID, when no institution was found. Without one of these two DIDs, the request answers 400.
beneficiary_id (required), stringThe id of your customer who received the funds.

4. If no institution sent it, name the sender

Record who your customer says sent the funds, and Notabene keeps the name on the transfer as IVMS-101. The call answers 204.

API reference
curl --request PATCH https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/parties/originator \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "party": {
      "id": "urn:uuid:92c6c2d2-fdad-44c2-8d20-25136a3c71f3",
      "name": "Ana Silva"
    },
    "reason": "Declared by our customer"
  }'const response = await fetch('https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/parties/originator', {
  method: 'PATCH',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "party": {
      "id": "urn:uuid:92c6c2d2-fdad-44c2-8d20-25136a3c71f3",
      "name": "Ana Silva"
    },
    "reason": "Declared by our customer"
  }),
});
const answer = response.status === 204 ? null : await response.json();import requests

response = requests.patch(
    'https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/parties/originator',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    json={
      "party": {
        "id": "urn:uuid:92c6c2d2-fdad-44c2-8d20-25136a3c71f3",
        "name": "Ana Silva"
      },
      "reason": "Declared by our customer"
    },
)
answer = None if response.status_code == 204 else response.json()
What to replace
  • 8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80: The transferId from step 3
  • urn:uuid:92c6c2d2-fdad-44c2-8d20-25136a3c71f3: An id of your choosing for the sender
  • Ana Silva: The name your customer declared
  • Declared by our customer: Why you add the sender, kept on the transfer’s timeline
Response204
HTTP/1.1 204 No Content
curl --request PATCH https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/parties/originator \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "party": {
      "id": "urn:uuid:92c6c2d2-fdad-44c2-8d20-25136a3c71f3",
      "name": "Ana Silva"
    },
    "reason": "Declared by our customer"
  }'
const response = await fetch('https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/parties/originator', {
  method: 'PATCH',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "party": {
      "id": "urn:uuid:92c6c2d2-fdad-44c2-8d20-25136a3c71f3",
      "name": "Ana Silva"
    },
    "reason": "Declared by our customer"
  }),
});
const answer = response.status === 204 ? null : await response.json();
import requests

response = requests.patch(
    'https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/parties/originator',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    json={
      "party": {
        "id": "urn:uuid:92c6c2d2-fdad-44c2-8d20-25136a3c71f3",
        "name": "Ana Silva"
      },
      "reason": "Declared by our customer"
    },
)
answer = None if response.status_code == 204 else response.json()
HTTP/1.1 204 No Content

5. If your policies flag it, review the transfer

Your policies evaluate the transfer like any other. Creating it answers Active, and within a second the transfer reads SettledIncompleteAuthorization because the funds are already there. It stays that way until every institution on it has authorized it, and then becomes Settled. With no institution to wait for, that can happen as soon as your policies approve it.

When your policies flag the transfer, a person on your team approves or rejects it, in the dashboard under Inbox › Transactions, or through the API.

curl https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/force-authorize \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "reason": "Declared sender reviewed"
  }'const response = await fetch('https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/force-authorize', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "reason": "Declared sender reviewed"
  }),
});
const answer = response.status === 204 ? null : await response.json();import requests

response = requests.post(
    'https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/force-authorize',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    json={
      "reason": "Declared sender reviewed"
    },
)
answer = None if response.status_code == 204 else response.json()
curl https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/reject \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "reason": "Sender not accepted"
  }'const response = await fetch('https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/reject', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "reason": "Sender not accepted"
  }),
});
const answer = response.status === 204 ? null : await response.json();import requests

response = requests.post(
    'https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/reject',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    json={
      "reason": "Sender not accepted"
    },
)
answer = None if response.status_code == 204 else response.json()
What to replace
  • 8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80: The transferId from step 3
  • Declared sender reviewed: Why your team approves it, kept on the transfer’s timeline
  • Sender not accepted: Why your team rejects it, kept on the transfer’s timeline
Reject response200
{
  "id": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
  "status": "Rejected",
  "direction": "Incoming",
  "counterparty": "did:web:originator.example:pt"
}
curl https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/force-authorize \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "reason": "Declared sender reviewed"
  }'
const response = await fetch('https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/force-authorize', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "reason": "Declared sender reviewed"
  }),
});
const answer = response.status === 204 ? null : await response.json();
import requests

response = requests.post(
    'https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/force-authorize',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    json={
      "reason": "Declared sender reviewed"
    },
)
answer = None if response.status_code == 204 else response.json()
curl https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/reject \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
  --header 'Content-Type: application/json' \
  --data '{
    "reason": "Sender not accepted"
  }'
const response = await fetch('https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/reject', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "reason": "Sender not accepted"
  }),
});
const answer = response.status === 204 ? null : await response.json();
import requests

response = requests.post(
    'https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80/reject',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
    json={
      "reason": "Sender not accepted"
    },
)
answer = None if response.status_code == 204 else response.json()
{
  "id": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
  "status": "Rejected",
  "direction": "Incoming",
  "counterparty": "did:web:originator.example:pt"
}

Both calls answer as they do on Withdrawals.

When you asked an institution for its data, the request only leaves once your side is approved. The institution then names its customer and sends the data, or rejects the request with a reason.

6. Credit your customer

Once every institution has authorized the transfer, its status changes to Settled and your webhook endpoint receives transfer.status_changed.v3. Events can repeat or arrive out of order, so handle them as described in Webhooks.

JSON
{
  "eventType": "transfer.status_changed.v3",
  "payload": {
    "id": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
    "for": "did:web:beneficiary.example:pt",
    "fromStatus": "SettledIncompleteAuthorization",
    "toStatus": "Settled",
    "settlementId": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d"
  }
}
{
  "eventType": "transfer.status_changed.v3",
  "payload": {
    "id": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
    "for": "did:web:beneficiary.example:pt",
    "fromStatus": "SettledIncompleteAuthorization",
    "toStatus": "Settled",
    "settlementId": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d"
  }
}

If you would rather poll, read the transfer until its status is Settled, then credit your customer.

API reference
curl https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80 \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'const response = await fetch('https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80', {
  method: 'GET',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
  },
});
const answer = response.status === 204 ? null : await response.json();import requests

response = requests.get(
    'https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
)
answer = None if response.status_code == 204 else response.json()
What to replace
  • 8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80: The transferId from step 3
Response200
{
  "id": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
  "status": "Settled",
  "direction": "Incoming",
  "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
  "requires_action": false
}
curl https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80 \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
const response = await fetch('https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80', {
  method: 'GET',
  headers: {
    Authorization: 'Bearer YOUR_ACCESS_TOKEN',
  },
});
const answer = response.status === 204 ? null : await response.json();
import requests

response = requests.get(
    'https://sandbox.notabene.id/api/v1/transfers/8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
)
answer = None if response.status_code == 204 else response.json()
{
  "id": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
  "status": "Settled",
  "direction": "Incoming",
  "settlement_id": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d",
  "requires_action": false
}

When a call fails

AnswerWhat it means
404 with address owner not foundNo institution registered the source address. Ask your customer who sent the funds.
400 with originator or originator_id is requiredThe request names neither the sending institution nor your own DID.
400 with cannot force-authorize a concluded txThe transfer is already Settled, so there is nothing to approve.
401The token is missing, malformed or expired. Authentication explains how to get a new one.
409 with a transfer with this ref already exists with different contentA transfer with this ref exists. Sending a create again says what it means.
AnswerWhat it means
404 with address owner not foundNo institution registered the source address. Ask your customer who sent the funds.
400 with originator or originator_id is requiredThe request names neither the sending institution nor your own DID.
400 with cannot force-authorize a concluded txThe transfer is already Settled, so there is nothing to approve.
401The token is missing, malformed or expired. Authentication explains how to get a new one.
409 with a transfer with this ref already exists with different contentA transfer with this ref exists. Sending a create again says what it means.

Common mistakes

MistakeWhat happensInstead
Creating the transfer without looking for one firstTwo transfers for the same deposit, when the sender had already created one.Look for a transfer in step 1, and retry within your window.
A new ref when you send a create againA second transfer for the same funds.Send the same request, with the same ref.
MistakeWhat happensInstead
Creating the transfer without looking for one firstTwo transfers for the same deposit, when the sender had already created one.Look for a transfer in step 1, and retry within your window.
A new ref when you send a create againA second transfer for the same funds.Send the same request, with the same ref.

How it works

Sent by an institution
No institution found
Steps
Sent by an institution
No institution found

On the counterparty’s side

Only when needed

← → the step before and after · Esc all steps
Step 1
1The funds land on your customer’s address
Your wallet, then your backend

Your wallet sees a deposit to one of your customers’ addresses, and your backend first checks that no transfer exists for it, retrying within a window you define. The call is in 1. Look for a transfer.

← → the step before and after · Esc all steps
Step 2
2Find out who sent it
Your backend, then your customer

Ask Notabene which institution holds the address the funds came from, and if none is found, ask your customer who sent the funds. The call is in 2. Find out who sent it.

← → the step before and after · Esc all steps
Sent by an institution · Step 3
3Ask the institution for its data
Your backend

Create the transfer as the beneficiary, naming the sending institution, and Notabene then sends it a request for the sender’s data. The call is in 3. Create the transfer.

← → the step before and after · Esc all steps
No institution found · Step 3–4
3–4Record the sender your customer declares
Your backend

Create the transfer with your own DID as the beneficiary’s institution, then add the sender your customer told you about. The calls are in 3. Create the transfer and 4. If no institution sent it, name the sender.

Good to know
  • There is no counterparty institution to wait for.
← → the step before and after · Esc all steps
Step 5
5Your policies approve it, or your team reviews it
Your policy engine, or your compliance team

Your policies evaluate the transfer like any other, and when they flag it, a person on your team reviews it. The calls are in 5. If your policies flag it, review the transfer.

Good to know
  • When you asked an institution for its data, the request only leaves once your side is approved.
← → the step before and after · Esc all steps
Only when needed
The institution answers
The sending institution, in its own systems

When you asked an institution, it finds which of its customers sent the funds, names them on the transfer and sends you their data. If it did not send the funds, it rejects the request with a reason.

Good to know
  • Its data only leaves once its own policies have approved the request.
  • If it does not answer, the requirement to receive its data is skipped after its time-out, and the transfer goes on without it.
← → the step before and after · Esc all steps
Step 6
6Credit your customer
Your backend

Once every institution on the transfer has authorized it, the transaction status changes from SettledIncompleteAuthorization to Settled, and you can credit your customer.

← → the step before and after · Esc all steps

Related

  • Transfers: what each institution sees on a transfer, and why statuses differ.
  • Statuses: every status and what your backend does at each.
  • Webhooks: every event and every requirement you may have to answer.
  • Errors: every error message and what to do.

Did this page help you?