Table of Contents
Installation
There are two options for loading the Notabene SDK:
<script id="notabene" async src="https://unpkg.com/@notabene/javascript-sdk@next/dist/notabene.js"></script>
Or installing the library:
Using Yarn:
yarn add @notabene/javascript-sdk
Using NPM:
npm install @notabene/javascript-sdk
If you installed the library into your project, you can import it into your project:
import Notabene from '@notabene/javascript-sdk';
Quick Start
const notabene = new Notabene({
nodeUrl: 'https://api.notabene.id',
authToken: 'YOUR_CUSTOMER_TOKEN',
});
const withdrawal = notabene.createWithdrawalAssist({
asset: 'ETH',
destination: '0x1234...',
amountDecimal: 1.0,
});
withdrawal.mount('#nb-withdrawal');
const { valid, value, txCreate } = await withdrawal.completion();
if (valid) {
}
Core Concepts
Authentication
Use the customer token endpoint with your access token to receive a token with a customer's scope.
⚠️ IMPORTANT ⚠️
When requesting the customer token
you must pass a unique customerRef
per customer for ownership proof reusability, otherwise you might encounter unwanted behavior.
Create a new Notabene instance:
const notabene = new Notabene({
nodeUrl: 'https://api.notabene.id',
authToken: '{CUSTOMER_TOKEN}',
locale: 'de',
});
Use the same nodeUrl
that you use to interact with the Notabene API.
General Component Usage
Each component can be used in various ways depending on your use case.
Embedded Component
This will let you embed the component into your existing withdrawal flow.
Create an html element to contain the component:
<div id="nb-withdrawal/>
Instantiate the withdrawal element and mount it using the id from above
const withdrawal = notabene.createWithdrawalAssist(tx, options);
withdrawal.mount('#nb-withdrawal');
The simplest way to get the result is to use:
try {
const { valid, value, txCreate, ivms101, proof } =
await withdrawal.completion();
if (valid) {
}
} catch (e) {
console.error(e);
}
Dynamic updates
To update the component as users enter transaction details:
withdrawal.update({
asset: 'ETH',
destination: '0x8d12a197cb00d4747a1fe03395095ce2a5cc6819',
amountDecimal: 1.12,
});
To be notified once the validation is completed so you can submit the withdrawal to your back end:
withdrawal.on('complete', { valid, value, txCreate, ivms101, proof } => ...)
To be notified of any validation errors use:
withdrawal.on('error',error => ...)
To be notified when the component is Ready:
withdrawal.on('ready', ({ type }) => {
console.log(type === 'ready');
});
Calling on
returns a function that will allow you to cleanly unsubscribe.
const unsubscribe = withdrawal.on('complete', { valid, value, txCreate, ivms101, proof } => ...)
unsubscribe()
Modal
All components support being opened in a modal using openModal()
, which returns a promise.
const withdrawal = notabene.createWithdrawalAssist(tx, options);
try {
const { valid, value, txCreate, ivms101, proof } =
await withdrawal.openModal();
if (valid) {
}
} catch (e) {
console.error(e);
}
All components support being opened in a popup window using popup()
, which returns a promise.
Many embedded wallets refuse to work in an iframe. In this case it is better to use a popup window.
Unfortunately there are also some restrictions on popup windows:
const withdrawal = notabene.createWithdrawalAssist(tx, options);
try {
const { valid, value, txCreate, ivms101, proof } = await withdrawal.popup();
if (valid) {
}
} catch (e) {
console.error(e);
}
Linked Component
In some cases, in particular institutional or mobile apps you may prefer to link your customers to the component through an email or redirect the user to it in a mobile app.
const withdrawal = notabene.createWithdrawalAssist(tx, options, {
callback:
redirectUri:
});
res.redirect(withdrawal.url);
Bear in mind that this is a full screen view for your users.
The two parameters that should be configured are:
callback
- a URL for your serverside. On completion this will receive an HTTP POST with the result as a json body and the authToken
as an Authorization: Bearer
header.redirectUri
- the user will be redirected here on completion. The result parameters will be json encoded in the URL fragment. You can use a mobile app schema to intercept these in your mobile app.
Note for data privacy reasons the callback will be coming from your users web browser and not from our infrastructure, so no static IP is currently possible. Instead please check the authToken
provided with the request.
Components
Assisted Withdrawal
The Withdrawal Assist component helps you collect additional required information from your user during a standard crypto withdrawal process.
const withdrawal = notabene.createWithdrawalAssist({
asset: 'ETH',
destination: '0x...',
amountDecimal: 1.23,
assetPrice: {
currency: 'USD',
price: 1700.12,
},
});
Parameters
asset
: The cryptocurrency or token being transferred. See Asset Specificationdestination
: The destination or blockchain address for the withdrawal. See DestinationamountDecimal
: The amount to transfer in decimal format. See Transaction AmountassetPrice
: Optional price information in a fiat currency. See Asset Price
If any of the required parameters are missing the component will just show the Notabene badge.
Configuration Options
Include configuration Options as a second optional parameter:
const withdrawal = notabene.createWithdrawalAssist(
{
asset: 'ETH',
destination: '0x...',
amountDecimal: 1.23,
assetPrice: {
currency: 'USD',
price: 1700.12,
},
},
{
proofs: {
microTransfer: {
destination: '0x...',
amountSubunits: '12344',
timeout: 86440,
},
},
},
);
See Transaction Options
Connect Wallet
The Connect Wallet component helps you collect and verify the address of your users self-hosted wallet in one go.
const connect = notabene.createConnectWallet({
asset: 'ETH',
});
const { proof, txCreate } = await connect.openModal();
Parameters
Configuration Options
Include configuration Options as a second optional parameter:
const connect = notabene.createConnectWallet(
{
asset: 'ETH',
},
{
proofs: {
microTransfer: {
destination: '0x...',
amountSubunits: '12344',
timeout: 86440,
},
},
},
);
Deposit Request
The Deposit Request lets your customers request deposits that are fully Travel Rule compliant.
const withdrawal = notabene.createDepositRequest({
asset: 'ETH',
destination: '0x...',
amountDecimal: 1.23,
customer: {
name: 'John Smith',
},
});
Parameters
asset
: The cryptocurrency or token being transferred. See Asset Specificationdestination
: The destination or blockchain address for the withdrawal. See DestinationamountDecimal
: Optional amount to deposit in decimal format. See Transaction Amountcustomer
: Optional Customer object containing their name
If any of the required parameters are missing the component will just show the Notabene badge.
Deposit Assist
The Deposit Assist component helps you collect missing Travel Rule data after a deposit has already been recorded on-chain. For example, if the deposit arrived with incomplete originator information, you can use Deposit Assist to request this information from your end-user.
const deposit = notabene.createDepositAssist(
{
asset: 'ETH',
amountDecimal: 1.23,
origin: '0x...',
destination: '0x...',
transactionId: "UUID"
},
{
},
);
Parameters
asset
: The cryptocurrency or token being transferred. See Asset Specificationorigin
: The origin or blockchain address for the deposit. See Origindestination
: The destination or blockchain address for the deposit. See DestinationamountDecimal
: Optional amount to deposit in decimal format. See Transaction AmounttransactionId
: Optional transactionId of a Notabene transaction. Will be returned with the payload to assist updating the Transaction
If any of the required parameters are missing the component will just show the Notabene badge.
Error handling
If any error occurs, the error
event is passed containing a message.
withdrawal.on('error', (error) => ...)
An example error object.
type Error = {
type: CMType.Error;
message: string;
description: string;
identifier: ErrorIdentifierCode;
};
Errors can be handled in this way:
component.on('error', (error) => {
if (error.type === CMType.Error) {
switch (error.identifier) {
case ErrorIdentifierCode.SERVICE_UNAVAILABLE:
break;
}
}
});
Error reference
ErrorIdentifierCode | Description/Message | In Use |
---|
SERVICE_UNAVAILABLE | The Notabene service is currently unavailable | ✅ |
TOKEN_INVALID | Auth Token is Invalid | ✅ |
WALLET_CONNECTION_FAILED | The connection to the wallet service failed, possibly due to network issues or unsupported wallet types. | ✅ |
WALLET_NOT_SUPPORTED | The wallet used does not support the required functionality or blockchain. | |
Transaction parameters
Asset specification
The asset
field the following types of assets specified:
notabene_asset
code passed as astring
. See Notabene Assets Service.- CAIP-19 is a chain agnostic format allows you to support the widest amount of assets and blockchains including NFTs.
- DTI is the ISO Digital Token Identifier format. See DTI registry for supported tokens.
Transaction amount
Use one of the following
amountDecimal
A number specifying the amount in decimal format. Eg. amountDecimal=1.1
would mean 1.1 of for example BTC or ETH.
Destination
Specify the beneficiary address as destination
using one of the following formats:
- CAIP-10 is a chain agnostic format allows you to specify the specific blockchain and address
- EIP-3770 EVM URI
- BIP-21 Bitcoin URI
- Native blockchain address
Origin
Specify the originator address as origin
using one of the following formats:
- CAIP-10 is a chain agnostic format allows you to specify the specific blockchain and address
- EIP-3770 EVM URI
- BIP-21 Bitcoin URI
- Native blockchain address
Asset Price
The price of the asset is used to determine certain rules based on thresholds. We recommond you pass in your price like this:
assetPrice: {
currency: 'USD',
price: 1700.12,
};
Configuration
Transaction Options
Some components can be configured using an optional TransactionOptions object.
The following shows the full set of options in typescript:
import Notabene, {
AgentType,
PersonType,
ProofTypes,
} from '@notabene/javascript-sdk';
const options: TransactionOptions = {
proofs: {
microTransfer: {
destination: '0x...',
amountSubunits: '12344',
timeout: 86440,
},
fallbacks: [ProofTypes.Screenshot, ProofTypes.SelfDeclaration],
deminimis: {
threshold: 1000,
currency: 'EUR',
proofTypes: [ProofTypes.SelfDeclaration],
},
},
allowedAgentTypes: [AgentType.PRIVATE, AgentType.VASP],
allowedCounterpartyTypes: [
PersonType.LEGAL,
PersonType.NATURAL,
PersonType.SELF,
],
fields: {
naturalPerson: {
name: true,
website: { optional: true },
email: true,
phone: true,
geographicAddress: false,
nationalIdentification: false,
dateOfBirth: false,
placeOfBirth: false,
countryOfResidence: true,
},
legalPerson: {
name: true,
lei: true,
website: { optional: true },
email: true,
phone: true,
geographicAddress: false,
nationalIdentification: false,
countryOfRegistration: true,
},
vasps: {
addUnknown: true,
onlyActive: true,
searchable: [
VASPSearchControl.ALLOWED,
VASPSearchControl.PENDING,
],
},
hide: [ValidationSections.ASSET, ValidationSections.DESTINATION],
},
};
const withdrawal = notabene.createWithdrawalAssist(tx, options);
The options can additionally be updated dynamically with the update()
function.
withdrawal.update(
{
asset: 'ETH',
destination: '0x8d12a197cb00d4747a1fe03395095ce2a5cc6819',
amountDecimal: 1.12,
},
{
proofs: {
microTransfer: {
destination: '0x...',
amountSubunits: '12344',
timeout: 86440,
},
},
},
);
Common use cases
Only allow first party transactions
const firstParty: TransactionOptions = {
allowedCounterpartyTypes: [
PersonType.SELF,
],
};
Only VASP to VASP transactions
const vasp2vasp: TransactionOptions = {
allowedAgentTypes: [AgentType.VASP],
};
Only Self-hosted wallet transactions
const options: TransactionOptions = {
allowedAgentTypes: [AgentType.PRIVATE],
};
Configuring ownership proofs
By default components support message signing proofs.
Supporting Micro Transactions (aka Satoshi tests)
You can support Micro Transfers (aka Satoshi tests) by adding a deposit address for the test.
Your compliance team will have to determine how to handle and verify these transactions in the rules engine or individually.
const options: TransactionOptions = {
proofs: {
microTransfer: {
destination: '0x...',
amountSubunits: '1234',
timeout: 86440,
},
fallbacks: [ProofTypes.Screenshot, ProofTypes.SelfDeclaration],
},
};
Notabene does not currently verify these tests automatically as you likely already have the infrastructure to do so.
You will receive a response back from the component containing a proof object. For MicroTransfers it will look like this:
type MicroTransferProof {
type: ProofTypes.MicroTransfer;
status: ProofStatus.PENDING;
did: DID;
address: CAIP10;
txhash: string;
chain: CAIP2;
amountSubunits: string;
}
Fallback Proof Options
You may accept a few options if none of the other are available. We do not recommend them, as they do not provide sufficient proof. However many VASPs do allow them for now:
const options: TransactionOptions = {
proofs: {
fallbacks: [ProofTypes.Screenshot, ProofTypes.SelfDeclaration],
},
};
The two options are:
screenshot
Where a user is requested to upload a screenshot of their walletself-declaration
Where a user self declares that they control the wallet address
Counterparty Field Properties
The fields requested from a customer about a counterparty can be configured with the fields object. You can configure required and optional fields individually for both natural and legal persons.
We recommend working closely with your compliance team for this. Bearing in mind that different jurisdictions have different rules.
Each field can be configured like this:
true
required fieldfalse
don't show{ optional: true }
show but don't require
Eg:
{
naturalPerson: {
website: { optional: true },
email: true,
phone: false,
}
}
The above will always ask the user for the following for natural persons:
name
since it is on by default (you can disable it explicitly by setting it to false
)website
is show but is optionalemail
is required
Full Example
const options: TransactionOptions = {
fields: {
naturalPerson: {
name: true,
website: { optional: true },
email: true,
phone: true,
geographicAddress: false,
nationalIdentification: false,
dateOfBirth: {
transmit: true,
},
placeOfBirth: false,
countryOfResidence: true,
},
legalPerson: {
name: true,
lei: true,
website: { optional: true },
email: true,
phone: true,
geographicAddress: false,
nationalIdentification: false,
countryOfRegistration: true,
},
},
};
Field reference
Field name | Natural | Legal | IVMS101 | description |
---|
name | ✅ | ✅ | 🟩 | Full name |
email | 🟩 | 🟩 | -- | Email (for your internal purposes) |
website | -- | ✅ | -- | Business Website (for your internal purposes) |
phone | 🟩 | 🟩 | -- | Mobile Phone (for your internal purposes) |
geographicAddress | 🟩 | 🟩 | 🟩 | Residencial or business address |
nationalIdentification | 🟩 | 🟩 | 🟩 | National Identification number |
dateOfBirth | 🟩 | -- | 🟩 | Date of birth |
placeOfBirth | 🟩 | -- | 🟩 | Place of birth |
countryOfResidence | 🟩 | -- | 🟩 | Country of Residence |
lei | -- | ✅ | 🟩 | LEI (Legal Entity Identifier) |
countryOfRegistration | -- | 🟩 | 🟩 | Country of Registration |
Locales
See locales for the list of supported locales.
Theming
You can optionally theme the UI when creating an instance of Notabene
const notabene = new Notabene({
nodeUrl: 'https://api.notabene.id',
authToken: 'YOUR_CUSTOMER_TOKEN',
theme: {
mode: 'light',
primaryColor: '#0a852d',
secondaryColor: '#f5f7fa',
fontFamily: 'Montserrat'
}
});
MIT © Notabene Inc.