Skip to content

Notabene

Notabene is one of Covalent's Travel Rule providers. Covalent uses Notabene's Transact v2 API to find the VASP that owns a destination address, exchange IVMS101 data with it, and authorize, reject and settle transfers. Notabene reports changes to Covalent with signed webhooks.

Covalent carries direct transfers between two VASPs only. See Known Limitations.

Field Value
Name notabene
Display name Notabene
Protocol notabene
Active true
Discovery Automatic wallet ownership discovery in the Notabene network
Transport OAuth REST API and Svix-signed webhooks

Each environment has its own credentials: a test key saves the test ones, a live key the live ones. All five fields are required.

Field Label Description
apiUrl Notabene API Region https://api.eu1.notabene.id (EU) or https://api.us1.notabene.id (US): the region hosting the environment's Notabene entity.
clientId Client ID The OAuth client ID Covalent gets access tokens with.
clientSecret Client Secret That client's secret.
vaspDid Entity DID The environment's Notabene entity, such as did:web:..., at most 255 characters.
webhookSecret Svix Webhook Signing Secret The endpoint signing secret from Notabene's webhook settings, starting with whsec_.

clientSecret and webhookSecret are secrets (password fields). The API returns no values; the dashboard shows the saved apiUrl, clientId and vaspDid to fill its form. Saving does not check the formats above: Covalent checks them when it uses the credentials, and a value that does not fit fails as INVALID_CONFIGURATION.

Terminal window
curl -X PUT "$COVALENT_URL/api/v2/providers/notabene" \
-H "x-api-key: $COVALENT_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"credentials": {
"apiUrl": "https://api.eu1.notabene.id",
"clientId": "...",
"clientSecret": "...",
"vaspDid": "did:web:acme.example",
"webhookSecret": "whsec_..."
}
}'

Covalent gets access tokens with the client credentials grant, from auth.notabene.id, or us.auth.notabene.id for the US region. A Notabene request times out after 15 seconds. Only a 401 is retried, once, with a new token.

POST /api/v2/providers/notabene/test gets an access token with the key's environment's credentials, then looks up vaspDid in the Notabene network. It succeeds when Notabene returns that same entity. The webhook signing secret is checked for its format only: a wrong one shows as callbacks answering 401.

A failed test's error is a code, such as Notabene request failed (HTTP_401). Covalent never passes on Notabene's response bodies, which can hold personal data.

Callers do not call Notabene endpoints directly. Save Notabene's credentials with PUT /api/v2/providers/notabene, or in the dashboard, and enable it with PATCH /api/v2/providers/notabene and a live key: one switch for both environments. See Providers and Integrations.

For a transfer Notabene carries:

  • provider.name is notabene; list such transfers with ?protocol=notabene.
  • cascade.externalId, and the externalId of its Notabene attempt, is Notabene's transfer ID.
  • The counterparty VASP's vaspId is its Notabene entity DID.

While Notabene is disabled, Covalent routes nothing through it, its callbacks answer 503, polling does not update its transfers, and actions on its transfers are refused.

Register this URL in Notabene's webhook settings for each environment's entity, and save that endpoint's signing secret as the environment's webhookSecret:

https://<your company's host>/api/system/trp/notabene/<environment>/<providerId>

<environment> is test or live. <providerId> is the provider's ID, the same in both environments: copy the path from the provider's callbackPaths, which lists it for the key's environment once the provider has been saved. Only POST is accepted. See Callback Paths.

Covalent processes an event only when:

  • the provider is yours, enabled, and has credentials for the path's environment;
  • the body is at most 256 KiB;
  • the Svix signature verifies with the environment's webhookSecret: an HMAC-SHA256 over the svix-id and svix-timestamp headers and the raw body, with a timestamp within five minutes of Covalent's clock;
  • the event has version 1.0.0, and a payload whose for is the environment's vaspDid and whose id is a Notabene transfer ID;
  • its message starts with notification.transfer or tap.require.

Covalent never takes a status from the event: it reads the transfer from Notabene and acts on that.

Status Body When
200 { "received": true } The event was processed, now or before.
200 { "ignored": true } A verified event with another message.
400 { "error": "Invalid JSON" } The body is not JSON.
400 { "error": "Invalid event or entity" } Not a Notabene event, or one for another entity.
401 { "error": "Invalid signature" } The signature is missing, wrong, or outside the five minutes.
404 { "error": "Not found", "code": "NOT_FOUND" } Another method or path.
413 { "error": "Body too large" } The body is over 256 KiB.
429 { "error": "Rate limit exceeded", "code": "RATE_LIMITED" } Your company's request budget, shared with /api/v2, is used up. See Retry-After.
503 { "error": "Provider unavailable" } The provider is not yours, is disabled, or has no credentials for the environment.
503 { "error": "<code>" } Processing failed: a code such as BENEFICIARY_WALLET_NOT_VERIFIED, or Webhook processing failed.

An event is recorded as processed, by its entity, svix-id and body, only once processing succeeds. The same delivery again answers { "received": true } and does nothing. A failed delivery is not recorded: delivered again, it is processed again.

When Notabene reports a transfer to your entity that Covalent does not hold yet:

  1. Covalent looks for the destination address, with its memo if any, among your wallets. The wallet must be verified and on the transfer's network, and its customer must have a verified Travel Rule identity. Otherwise no transfer is created, and the event answers 503.
  2. Covalent creates an incoming transfer in exchanging, with compliance pending, and sends a transfer.created webhook.
  3. If the originating VASP named the beneficiary, each name must match your customer's verified identity, ignoring case, spaces and punctuation. Otherwise processing stops with BENEFICIARY_NAME_MISMATCH, and nothing is sent.
  4. Covalent confirms the address's relationship to your entity on Notabene, and appends your customer's verified identity as the beneficiary.
  5. Covalent screens the originator information Notabene holds. With an approved verdict, Covalent authorizes the transfer on Notabene without an officer: awaiting_counterparty. A pending verdict holds it for an officer. A rejected verdict holds it, and nothing is sent to Notabene.
  6. The transfer is accepted once the originating VASP is AUTHORIZED, SETTLED or CLEARED on Notabene, and completed once Notabene shows the transfer SETTLED or CLEARED with a settlement. Its txHash comes from the settlement ID; an incoming transfer does not take a txHash from you.

POST /api/v2/transfers does not name a provider. Notabene can carry a transfer with:

Capability Value
Beneficiary address Required
Originator address Optional
Beneficiary IVMS101 Required: at least one person in beneficiary.beneficiaryPersons
Beneficiary VASP Not required upfront: discovered from the address
Destination memo Supported

With "preferredProtocol": "notabene":

  • The create is refused with 400 VALIDATION_ERROR unless Notabene is enabled, has credentials for the key's environment, and can carry the transfer. Without beneficiary persons, error reads Preferred protocol "notabene" cannot handle this request: beneficiary IVMS data is required.
  • Covalent tries Notabene first, unless the beneficiary VASP was last reached through another provider: that one goes first.
  • Another enabled provider may still carry the transfer when Notabene finds no VASP for the address, or when the Notabene attempt stops before Covalent creates the transfer there, for example because the originator customer has no verified Travel Rule identity.
  • Once Covalent has started creating the transfer on Notabene, no other provider is tried: a failure makes the transfer failed.

While Notabene is enabled with credentials for the environment, Covalent asks it who owns the beneficiary address of every outgoing transfer, whatever preferredProtocol names. It asks about the asset on its network, such as USDC-POLYGON, and the address, followed by :<memo> when there is a destination memo.

Notabene's answer Effect
CONFIRMED, by a VASP other than yours, with no custodian That VASP is the beneficiary VASP. Covalent looks up its name in the Notabene network.
NOT_FOUND, or an asset on a network Covalent does not send through Notabene No VASP on Notabene; another provider may carry the transfer.
UNCONFIRMED, a custodian, your own entity, an answer about another address, memo or asset, or a refusal (a 4xx other than 429) Routing stops: the transfer is failed, no provider carries it, and no customer data is sent.
None: a timeout, a 429 or a 5xx Unless another provider found the VASP, the transfer job asks again at least 11 seconds later, for up to five minutes; then the transfer is failed.

Covalent sends assets through Notabene on these networks only: bitcoin, ethereum, polygon, solana, tron, bsc, avalanche, arbitrum, optimism, base, litecoin, ripple, stellar, algorand, dogecoin, cardano, cosmos, near and polkadot.

  1. Covalent creates the transfer on Notabene, with the Covalent transfer ID as its ref.
  2. Covalent appends the IVMS101 data: your customer's verified Travel Rule identity as the originator, not the request's originator, with the originator address if given; and the request's beneficiary persons, with the beneficiary address. The transfer is exchanging.
  3. Covalent screens the beneficiary information Notabene holds, once there is some. An approved verdict authorizes the transfer on Notabene: awaiting_counterparty. A pending or rejected verdict holds it.
  4. The transfer is accepted once the beneficiary VASP is AUTHORIZED, SETTLED or CLEARED on Notabene.
  5. Confirm the blockchain transaction with accept and its txHash, as below.

For a transfer Notabene carries, PATCH /api/v2/transfers/:id acts on Notabene before it changes anything. When Notabene refuses, cannot be reached, or a check fails, the action answers 400 INVALID_REQUEST with the reason in error, such as Notabene request failed (AUTHORIZATION_PENDING), and the transfer is unchanged. See Transfer Actions.

Action Takes On Notabene, then in Covalent
accept An incoming transfer, held with compliance pending Authorizes the transfer and reads it back: it must be AUTHORIZED, SETTLED or CLEARED. The transfer moves to awaiting_counterparty, with compliance approved.
reject An incoming transfer, held with compliance pending Rejects it with the reason SANCTION_SCREENING for SANCTIONS_MATCH, else COMPLIANCE_POLICIES; details are not sent. The transfer is rejected.
accept with txHash An outgoing transfer, awaiting_counterparty or accepted, with compliance approved and status.theirs accepted Settles it with the hash as settlement ID and reads it back: it must be SETTLED or CLEARED with that hash. The transfer is completed.
Code Meaning
LOCAL_APPROVAL_REQUIRED The originator information on Notabene is missing or changed since screening, Notabene flags the transfer, or its state or verdict does not allow it.
VERIFIED_BENEFICIARY_IDENTITY_REQUIRED Your customer has no verified Travel Rule identity.
AUTHORIZATION_PENDING Notabene took the request but does not show the transfer authorized.
ALREADY_SETTLED The transfer has a transaction hash or a settlement: it cannot be rejected.
TRANSFER_NOT_AUTHORIZED Notabene does not show the transfer and the beneficiary VASP authorized, the beneficiary information changed since screening, or Notabene flags the transfer.
TX_HASH_CONFLICT Another hash was already reported for the transfer.
SETTLEMENT_PENDING Notabene does not show the transfer settled with this hash.
TRANSFER_BINDING_MISMATCH The Notabene transfer's amount, asset, network, addresses, memo or VASPs differ from the Covalent transfer's.

A hash Covalent has tried to settle with is kept: a later confirm must use the same hash. Resolving the case of a transfer held for review as approved moves it to awaiting_counterparty without calling Notabene; Covalent authorizes it on Notabene at its next event or poll.

Covalent reads the Notabene transfer at each event, and once a minute for a transfer that is exchanging or awaiting_counterparty and unchanged for five minutes. It applies Notabene's status this way:

Notabene Covalent
The transfer, or the counterparty VASP, is REJECTED rejected, with status.theirs rejected.
FLAGGED, FLAGGED-SETTLEMENT, FROZEN, REVERTED, REVERT-REQUESTED, REVERT-AUTHORIZED, REVERT-REJECTED, REVERT-FLAGGED, or any flag on the transfer held, with compliance pending; a rejected verdict stays.
After Covalent's authorization: the counterparty VASP is AUTHORIZED, SETTLED or CLEARED status.theirs is accepted; awaiting_counterparty becomes accepted.
Incoming, once accepted: the transfer is SETTLED or CLEARED with a settlement completed, with the settlement's transaction hash.

When Covalent authorizes the transfer on Notabene, status.ours becomes accepted and exchanging becomes awaiting_counterparty. Counterparty information that is new or has changed is screened before Covalent authorizes; a change after acceptance holds the transfer for an officer's approval again.

  • Covalent appends IVMS101 data to the Notabene transfer and reads the counterparty's with Notabene's decrypt=true: it does not encrypt the data for Notabene itself. Enable Notabene-managed PII encryption for your entity, as the dashboard's help for webhookSecret says.
  • Your customer's data is always their verified Travel Rule identity: as the originator of an outgoing transfer, and as the beneficiary of an incoming one. An outgoing transfer also carries the beneficiary persons of your create request.
  • Before screening the counterparty's IVMS101 data, Covalent checks that its persons form a valid group and that the account numbers it lists include the transfer's address (INVALID_PEER_IDENTITY, PII_ACCOUNT_MISMATCH).
  • Covalent stores the IVMS101 data it sends and receives encrypted at rest.
  • Only direct transfers between two VASPs: a Notabene transfer with a custodian, a gateway, or more than one VASP for a party is refused (UNSUPPORTED_AGENT_CHAIN).
  • Covalent cannot reject an outgoing transfer that Notabene carries: reject takes incoming transfers only, and rejecting its case leaves it held. A transfer whose screening verdict is rejected stays held: Covalent does not reject it on Notabene, and reject does not take it.
  • A transfer still exchanging 15 minutes after its last change, for example while Notabene holds no counterparty information for it, is marked failed. A failed transfer that Notabene carries cannot be retried, and Notabene's later events for it answer 503.
  • When the Notabene attempt fails after Covalent started creating the transfer there, for example because Notabene's answer was lost, the transfer is failed, its Notabene attempt is submission_unknown, and a retry does not send it again. A later Notabene event for that transfer binds it and resumes it.
  • An accepted incoming transfer completes on a Notabene event only: polling covers exchanging and awaiting_counterparty.