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.
Provider Config
Section titled “Provider Config”| 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 |
Required Provider Fields
Section titled “Required Provider Fields”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.
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.
Connection Test
Section titled “Connection Test”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.
Public API Relationship
Section titled “Public API Relationship”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.nameisnotabene; list such transfers with?protocol=notabene.cascade.externalId, and theexternalIdof its Notabene attempt, is Notabene's transfer ID.- The counterparty VASP's
vaspIdis 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.
Callbacks
Section titled “Callbacks”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 thesvix-idandsvix-timestampheaders and the raw body, with a timestamp within five minutes of Covalent's clock; - the event has
version1.0.0, and apayloadwhoseforis the environment'svaspDidand whoseidis a Notabene transfer ID; - its
messagestarts withnotification.transferortap.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.
Incoming Transfers
Section titled “Incoming Transfers”When Notabene reports a transfer to your entity that Covalent does not hold yet:
- 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. - Covalent creates an incoming transfer in
exchanging, with compliancepending, and sends atransfer.createdwebhook. - 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. - Covalent confirms the address's relationship to your entity on Notabene, and appends your customer's verified identity as the beneficiary.
- Covalent screens the originator information Notabene holds. With an
approvedverdict, Covalent authorizes the transfer on Notabene without an officer:awaiting_counterparty. Apendingverdict holds it for an officer. Arejectedverdict holds it, and nothing is sent to Notabene. - The transfer is
acceptedonce the originating VASP isAUTHORIZED,SETTLEDorCLEAREDon Notabene, andcompletedonce Notabene shows the transferSETTLEDorCLEAREDwith a settlement. ItstxHashcomes from the settlement ID; an incoming transfer does not take atxHashfrom you.
Outgoing Transfers
Section titled “Outgoing Transfers”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_ERRORunless Notabene is enabled, has credentials for the key's environment, and can carry the transfer. Without beneficiary persons,errorreadsPreferred 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.
Discovery
Section titled “Discovery”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.
Sending
Section titled “Sending”- Covalent creates the transfer on Notabene, with the Covalent transfer ID as its
ref. - 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'sbeneficiarypersons, with the beneficiary address. The transfer isexchanging. - Covalent screens the beneficiary information Notabene holds, once there is some. An
approvedverdict authorizes the transfer on Notabene:awaiting_counterparty. Apendingorrejectedverdict holds it. - The transfer is
acceptedonce the beneficiary VASP isAUTHORIZED,SETTLEDorCLEAREDon Notabene. - Confirm the blockchain transaction with
acceptand itstxHash, as below.
Accept, Reject and Confirm
Section titled “Accept, Reject and Confirm”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.
Status Codes
Section titled “Status Codes”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.
Personal Data
Section titled “Personal Data”- 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 forwebhookSecretsays. - 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
beneficiarypersons 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.
Known Limitations
Section titled “Known Limitations”- 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:
rejecttakes incoming transfers only, and rejecting its case leaves itheld. A transfer whose screening verdict isrejectedstaysheld: Covalent does not reject it on Notabene, andrejectdoes not take it. - A transfer still
exchanging15 minutes after its last change, for example while Notabene holds no counterparty information for it, is markedfailed. Afailedtransfer that Notabene carries cannot be retried, and Notabene's later events for it answer503. - 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 issubmission_unknown, and a retry does not send it again. A later Notabene event for that transfer binds it and resumes it. - An
acceptedincoming transfer completes on a Notabene event only: polling coversexchangingandawaiting_counterparty.