Statuses and decisions in the API

Your compliance team works with policies, requirements and statuses in the dashboard, as Transfers, Policies, Requirements and Statuses explain. This page is the same ground for your backend: where it reads each of them on a transfer, and the calls that decide one.

Read a transfer

On the transferIn the API
Your verdictYour audit trail, as transaction.compliance.evaluated with its decision.
Your statusThe status of your entry in agents, the one with your DID and the role VASP. Other entries, such as the settlement address, do not count towards any status.
RequirementsUnder policies, in the entry of the institution that set them: type, status, and from_did, the institution that must meet it.
Transaction statusThe transfer’s status.
Something waits on yourequires_action is true.
What your backend does nextnext_action.
On the transferIn the API
Your verdictYour audit trail, as transaction.compliance.evaluated with its decision.
Your statusThe status of your entry in agents, the one with your DID and the role VASP. Other entries, such as the settlement address, do not count towards any status.
RequirementsUnder policies, in the entry of the institution that set them: type, status, and from_did, the institution that must meet it.
Transaction statusThe transfer’s status.
Something waits on yourequires_action is true.
What your backend does nextnext_action.

The same requirement names its two institutions differently in a transfer and in an event:

WhereThe institution that set itThe institution that must meet it
A transfer you readThe entry in agents the requirement sits underfrom_did
The requirements.pending.v3 eventfromagentId
WhereThe institution that set itThe institution that must meet it
A transfer you readThe entry in agents the requirement sits underfrom_did
The requirements.pending.v3 eventfromagentId
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 transfer’s id
Response200
{
  "id": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
  "type": "Transfer",
  "status": "Active",
  "direction": "Incoming",
  "counterparty": "did:web:originator.example:pt",
  "asset": "eip155:1/slip44:60",
  "amount": "0.5",
  "requires_action": true,
  "next_action": {
    "type": "Review"
  },
  "agents": [
    {
      "agent_did": "did:web:originator.example:pt",
      "role": "VASP",
      "status": "Authorized",
      "policies": [
        {
          "type": "WalletAddressConfirmation",
          "status": "Satisfied",
          "from_did": "did:web:beneficiary.example:pt"
        }
      ]
    },
    {
      "agent_did": "did:web:beneficiary.example:pt",
      "role": "VASP",
      "status": "Pending",
      "policies": [
        {
          "type": "ReceiveTRInfo",
          "status": "Pending",
          "from_did": "did:web:originator.example:pt"
        },
        {
          "type": "Authorization",
          "status": "Satisfied",
          "from_did": "did:web:originator.example:pt"
        },
        {
          "type": "BeneficiaryNameMatch",
          "status": "Pending",
          "from_did": "did:web:beneficiary.example:pt"
        }
      ]
    }
  ]
}
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",
  "type": "Transfer",
  "status": "Active",
  "direction": "Incoming",
  "counterparty": "did:web:originator.example:pt",
  "asset": "eip155:1/slip44:60",
  "amount": "0.5",
  "requires_action": true,
  "next_action": {
    "type": "Review"
  },
  "agents": [
    {
      "agent_did": "did:web:originator.example:pt",
      "role": "VASP",
      "status": "Authorized",
      "policies": [
        {
          "type": "WalletAddressConfirmation",
          "status": "Satisfied",
          "from_did": "did:web:beneficiary.example:pt"
        }
      ]
    },
    {
      "agent_did": "did:web:beneficiary.example:pt",
      "role": "VASP",
      "status": "Pending",
      "policies": [
        {
          "type": "ReceiveTRInfo",
          "status": "Pending",
          "from_did": "did:web:originator.example:pt"
        },
        {
          "type": "Authorization",
          "status": "Satisfied",
          "from_did": "did:web:originator.example:pt"
        },
        {
          "type": "BeneficiaryNameMatch",
          "status": "Pending",
          "from_did": "did:web:beneficiary.example:pt"
        }
      ]
    }
  ]
}

Here the beneficiary’s institution reads a deposit that the originator’s institution has already authorized. Its own side is still Pending, because the Travel Rule data has not arrived and the name check is open.

What to do next

When you read a transfer, next_action says what your backend does next, and requires_action says whether a decision waits on a person on your team. Both are on GET /api/v1/transfers/{id}, not on the list of transfers.

TypeWhat it meansWhat you do
ProvideInformationYour customer’s Travel Rule data is incomplete, or the counterparty asked for it.Complete it with PATCH/api/v1/transfers/{id}/parties/{role}, which takes the data as party.ivms, then send it with POST/api/v1/transfers/{id}/send-presentation.
ConfirmRelationshipThe counterparty asked you to confirm the settlement address.Answer it as Requirements you resolve describes, or register the address as Wallets explains.
ReviewA person on your team decides. requires_action is true.Nothing for your backend to do unless it makes the decision, with the calls under Decide a transfer.
ExecuteSettlementThe transfer is ready to settle, and you send the funds. next_action.settlement holds the asset, the amount, the address and any memo_tag.Send the funds, then record the transaction hash with POST/api/v1/transfers/{id}/settle.
TypeWhat it meansWhat you do
ProvideInformationYour customer’s Travel Rule data is incomplete, or the counterparty asked for it.Complete it with PATCH /api/v1/transfers/{id}/parties/{role}, which takes the data as party.ivms, then send it with POST /api/v1/transfers/{id}/send-presentation.
ConfirmRelationshipThe counterparty asked you to confirm the settlement address.Answer it as Requirements you resolve describes, or register the address as Wallets explains.
ReviewA person on your team decides. requires_action is true.Nothing for your backend to do unless it makes the decision, with the calls under Decide a transfer.
ExecuteSettlementThe transfer is ready to settle, and you send the funds. next_action.settlement holds the asset, the amount, the address and any memo_tag.Send the funds, then record the transaction hash with POST /api/v1/transfers/{id}/settle.

next_action is null when nothing of yours is pending, as when the counterparty is holding the transfer. It is also null before your policies reach a verdict, once the transfer has concluded, and on a payment.

Requirements

Each requirement carries its name in type:

RequirementIn the API
Send Travel Rule dataSendTRInfo
Transmit Travel Rule dataTransmitTRData
Receive Travel Rule dataReceiveTRInfo
Submit Travel Rule dataSubmitTRInfo
Confirm beneficiary name matchBeneficiaryNameMatch
Complete internal compliance checksInternalComplianceCheck
Require wallet ownership confirmationWalletAddressConfirmation
Require wallet ownership confirmation, added by NotabeneWalletConfirmationForDisclosure
Require counterparty authorizationAuthorization
RequirementIn the API
Send Travel Rule dataSendTRInfo
Transmit Travel Rule dataTransmitTRData
Receive Travel Rule dataReceiveTRInfo
Submit Travel Rule dataSubmitTRInfo
Confirm beneficiary name matchBeneficiaryNameMatch
Complete internal compliance checksInternalComplianceCheck
Require wallet ownership confirmationWalletAddressConfirmation
Require wallet ownership confirmation, added by NotabeneWalletConfirmationForDisclosure
Require counterparty authorizationAuthorization

In the API, a requirement’s status reads Pending, Satisfied, Skipped, Rejected, NotApplicable for N/A, or Incomplete when it is partially satisfied.

Answer a requirement

Three requirements take your backend’s answer, all through the same call, as Requirements you resolve describes with each outcome.

RequirementIn the pathYour answer
Confirm wallet ownershipwallet-address-confirmationconfirmed, or rejected when the address is not your customer’s
Confirm beneficiary name matchbeneficiary-name-matchmatched, partial_match (held for review) or mismatch
Complete internal compliance checksinternal-compliance-checkcompleted or rejected
RequirementIn the pathYour answer
Confirm wallet ownershipwallet-address-confirmationconfirmed, or rejected when the address is not your customer’s
Confirm beneficiary name matchbeneficiary-name-matchmatched, partial_match (held for review) or mismatch
Complete internal compliance checksinternal-compliance-checkcompleted or rejected

Registering an address in advance, as Wallets explains, confirms it by itself. A mismatch rejects the transfer and tells the sender why with the reason code BE01, and partial_match exists only in the API. The call is in the API reference as Answer a requirement.

Submit Travel Rule data takes no answer, because adding the sender’s data meets it, as described in Deposits, funds first.

Decide a transfer

Three calls decide a transfer by hand, and they are easy to confuse:

ToCallWhen
Authorize a transfer your policies flagged, or with requirements openPOST/api/v1/transfers/{id}/force-authorize, with a reasonIt overrides the flag, and every open requirement your institution set then reads Skipped.
Accept a transfer that asks for your authorizationPOST/api/v1/transfers/{id}/authorizeWhen the counterparty requires your authorization: an Authorization requirement whose from_did is your DID.
Reject a transferPOST/api/v1/transfers/{id}/rejectIn place of either of the two above.
ToCallWhen
Authorize a transfer your policies flagged, or with requirements openPOST /api/v1/transfers/{id}/force-authorize, with a reasonIt overrides the flag, and every open requirement your institution set then reads Skipped.
Accept a transfer that asks for your authorizationPOST /api/v1/transfers/{id}/authorizeWhen the counterparty requires your authorization: an Authorization requirement whose from_did is your DID.
Reject a transferPOST /api/v1/transfers/{id}/rejectIn place of either of the two above.

Requirements another institution set for you stay open after a force-authorize, and so does the hold on personal data. Your audit trail records each verdict as transaction.compliance.evaluated, whose details.decision is Authorize, Flag or Reject, and writes Approve as Authorize. Withdrawals shows these calls within a flow.

Read your policies

API reference
curl https://sandbox.notabene.id/api/v1/policy-configs \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'const response = await fetch('https://sandbox.notabene.id/api/v1/policy-configs', {
  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/policy-configs',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
)
answer = None if response.status_code == 204 else response.json()
Response200
[
  {
    "policy_name": "AmountThreshold",
    "kind": "Standard",
    "type": "Transaction",
    "is_enabled": true,
    "config": {
      "basis": "",
      "approve_below": 0,
      "flag_above": 10000,
      "reject_above": 100000
    }
  },
  {
    "policy_name": "GeneralDefault",
    "kind": "Standard",
    "type": "Transaction",
    "is_enabled": true,
    "config": {
      "verdict": "Flag"
    }
  }
]
curl https://sandbox.notabene.id/api/v1/policy-configs \
  --header 'Authorization: Bearer YOUR_ACCESS_TOKEN'
const response = await fetch('https://sandbox.notabene.id/api/v1/policy-configs', {
  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/policy-configs',
    headers={'Authorization': 'Bearer YOUR_ACCESS_TOKEN'},
)
answer = None if response.status_code == 204 else response.json()
[
  {
    "policy_name": "AmountThreshold",
    "kind": "Standard",
    "type": "Transaction",
    "is_enabled": true,
    "config": {
      "basis": "",
      "approve_below": 0,
      "flag_above": 10000,
      "reject_above": 100000
    }
  },
  {
    "policy_name": "GeneralDefault",
    "kind": "Standard",
    "type": "Transaction",
    "is_enabled": true,
    "config": {
      "verdict": "Flag"
    }
  }
]

Each policy carries its policy_name, whether it is_enabled, and its settings in config. Here a transfer above 10,000 is flagged, one above 100,000 is rejected, and one that no policy has an opinion on is flagged too.

Follow the status

Each time the transaction status changes, your webhook endpoint receives transfer.status_changed.v3 with the status before and after. When your policies hold the transfer for review, the transfer reads requires_action: true until your team decides. Webhooks describes the other events.

JSON
{
  "eventType": "transfer.status_changed.v3",
  "payload": {
    "id": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
    "for": "did:web:example.com:pt",
    "fromStatus": "ReadyToSettle",
    "toStatus": "Settled",
    "settlementId": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d"
  }
}
{
  "eventType": "transfer.status_changed.v3",
  "payload": {
    "id": "8f3c2a1e-5b7d-4c90-a1f2-3e4d5c6b7a80",
    "for": "did:web:example.com:pt",
    "fromStatus": "ReadyToSettle",
    "toStatus": "Settled",
    "settlementId": "eip155:1:tx/0x7d3c9e1f0a2b4c6d8e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c2d"
  }
}
Transaction statusWhat your backend does
ActiveNothing, unless something waits on you: then the transfer’s requires_action is true and you receive requirements.pending.v3.
ReadyToSettleAs the sender, send the funds and record the transaction hash with POST/api/v1/transfers/{id}/settle.
Rejected, CanceledDo not send the funds.
SettledAs the beneficiary, credit your customer.
SettledIncompleteAuthorizationKeep answering what is asked. A transfer you create from a deposit is here within a second of being created.
Transaction statusWhat your backend does
ActiveNothing, unless something waits on you: then the transfer’s requires_action is true and you receive requirements.pending.v3.
ReadyToSettleAs the sender, send the funds and record the transaction hash with POST /api/v1/transfers/{id}/settle.
Rejected, CanceledDo not send the funds.
SettledAs the beneficiary, credit your customer.
SettledIncompleteAuthorizationKeep answering what is asked. A transfer you create from a deposit is here within a second of being created.

Revert a transfer

To return the funds of a settled transfer, request a revert with POST /api/v1/transfers/{id}/revert and a reason, which is shared with the counterparty. The revert is a new transfer linked to the original, and the funds go back to the original source_address or to the settlement_address you give. Statuses lists the revert statuses.


Did this page help you?