Sygna Bridge
API Design Principles
API Request and Response
REST-like API - Simple and predictable URLs to access data. The Sygna Bridge APIs uses GET and POST, and HTTP responses are UTF-8 encoded JSON objects.
Securing the APIs by using HTTPS
Hypertext Transfer Protocol Secure (HTTPS) is a variant of the standard web transfer protocol (HTTP) that adds a layer of security on the data in transit through the SSL/TLS protocol connection. Sygna Bridge APIs use HTTPS to provide a stronger guarantee that a VASP is communicating with the real API and receiving back authentic content. It also enhances privacy for applications and VASPs using the API.
Callback APIs
VASPs require best-time updates and should avoid busy-waiting for the other party's compliance validation. We use a callback method to work around this issue and flag the VASP so it can proceed.
The beneficiary server can take as long as it needs to validate the permission request. When it finishes the validation, it will post the permission to Sygna Bridge's callback url, and Sygna Bridge will then relay the permission to the originator's callback url.
Authentication
We implement API Authentication with API key. All VASPs must attach a valid api_key to every API call.
Data Confidentiality and Integrity
ECIES Encryption
const sygnaBridgeUtil = require('sygna-bridge-util');
const sensitive_data = {
"originator": {
"name": "Antoine Griezmann",
"date_of_birth":"1991-03-21"
},
"beneficiary":{
"name": "Leo Messi"
}
};
const recipient_pubKey = "04b1f14590a37c5c5fdcdc4f6d606eb383a79d5f6d72c210ec4fab47c2e9a59b4fd1149d8e8fa31ac1a04a9142cda2a479c642fb606eaac14c874fd7426e379f54"
const private_info = sygnaBridgeUtil.crypto.sygnaEncodePrivateObj(sensitive_data, recipient_pubKey);
// 04acf400bab2961d3e7b801ee562e582274b7a8bdc9737e6b72cfaa9dc21cfde0a0ccf0d7b3a061814e157b11eb7c4b986765a10b1d4b0b9e0dbd0376a16a16162a8b6e9ff0319aef48af93522c33a4692e6e3a903de2d925a4580d304804a7cb3c191a61e212fae387f66f737fe3f97b05d960878515bfe391076fff6f588429f016321b96374049b72d2f6bb127c5a4488f0caec32dae9bc4f6289fcc78bf280b26957cda6013be81009acc07fee246aff0ace7f7016d99f9289b4410990d505d738ef4e
// const decoded_priv_info = sygnaBridge.crypto.sygnaDecodePrivateObg(private_info, recipient_privKey);
We apply Elliptic Curve Integrated Encryption Scheme (ECIES) to encrypt all sensitive data between communication of VASPs.
This following diagram shows how ECIES works. ECC public/private key cryptography is used in conjunction with the Diffie-Hellman primitive to allow both sides to independently generate a shared secret key, for one-time use to encrypt data with a symmetric key algorithm.
We have provided a JavaScript library on NPM to handle ECIES encryption and decryption. You can take a look at the sample usage on the right or visit our Github for more information.
ECDSA Signature
const sygnaBridgeUtil = require('sygna-bridge-util');
// POST Txid Signature Example
const transfer_id = "325592beef10daf73790b19cf3c4050669b9bfe4b11c45de7c40cebd792a905e";
const txid = "1a0c9bef489a136f7e05671f7f7fada2b9d96ac9f44598e1bcaa4779ac564dcd";
const myPrivKey = "61019d9bfdaecefab4b35273fa06c4baaa9451996611ff7a2904c252213ce544"
const TxIdObj = sygnaBridgeUtil.crypto.signSendTxId(transfer_id, txid, myPrivKey);
console.log(TxIdObj.signature);
//37d2b7fc42d863ba126834945f4bee6d17b249cb8b3bb09ded4defc7aea439e3469c84a140e73ed78a0c7abee1a819c5db9d7c9413640fcca9d766eff633de20
We apply an ECDSA signature to ensure data integrity. All API calls must attach a signature signed by the requesting VASP's local private key, and every VASP can verify if the message received is valid by checking the signature and the corresponding public key.
The Digital Signature also provides message authentication (the receiver can verify the originator of the message) and non-repudiation (the sender cannot falsely claim that they have not signed the message).
Our signing algorithm uses secp256k1 Elliptic Curve with SHA256 Hash function. The details of the signing process can also be found on our Github.
With our library, we provide simple interfaces to deal with input parameters, help you convert them into JSON object strings and then go through the signing process. You will need to use different interfaces to get signatures of different API requests. These interfaces generally return a signed object, which has already inserted the signature for you, and will be ready to put into a request body.
Sygna Bridge API
API Flow
API Endpoint
{sygna-bridge production domain} = https://api.sygna.io/sb/
{sygna-bridge staging domain} = https://staging-api.sygna.io/sb/
Sygna Bridge API helps VASP to exchange required compliance informations and obtain a cryptographically signed compliance record (we call it permission) before broadcasting anything to the blockchain. The complete flow works as follows:
VASP Registration and Verification
In order to use our API, you will need to contact us to request an API key.
After you successfully register as a verified VASP in the Sygna Bridge Ecosystem, we will publish your public key associate with a vasp_code
for any other VASP to query.
Before you start a permission request, you will need to know the transaction beneficiary's corresponding VASP and the public key of that VASP.
Main Permission Request Flow
During the main process of Signa Bridge's Permission Request, the originator of a cryptocurrency transaction first starts a permissionRequest
, and posts it to the Sygna Bridge central server. The server will relay the request to the beneficiary VASP and wait for the callback of the validation result.
Whenever the beneficiary finishes the request validation, they will post a permission
back to Sygna Server. The server will store the data and relay the message to the originator VASP's server and complete the process.
Submit Transaction ID
After the originator VASP has successfully received the permission and a signature from the beneficiary VASP, the originator VASP can proceed with the transaction and broadcast it to the blockchain. After the transaction has been confirmed by the network, the originator should submit the on-chain transaction ID to Sygna Bridge for future query.
GET VASP
const fetch = require('node-fetch')
const url = "https://staging-api.sygna.io/sb/api/v1/bridge/vasp"
let response = await fetch(url, { "api_key":"4973a45555f3200b34937b5ae27 c0da8653a584271fda1869262fc7edad8b88d" })
response = await response.json();
Sample response
{
"signature": "3044022007d96bd01bcbcb11149a8871370617c840a74a09bb6b66da5681748b6a0b58f902203de2977c12609761d91cbcd769d8f6e7315d33bbe0c6220d5428d9c2d5c5a8ab",
"vasp_data": [
{
"vasp_name": "Exchange1",
"vasp_code": "VASPUSNY1",
"vasp_pubkey": "048709ef46f46c7e89b58987b606dc54eda62f88424517667305d91b3e86b8847f1b44a9659831880a15885ec43a722f76c356ec0ee373a273a0a7900dcd077339"
},
{
"vasp_name": "Exchange2",
"vasp_code": "VASPUSNY2",
"vasp_pubkey": "04b1f14590a37c5c5fdcdc4f6d606eb383a79d5f6d72c210ec4fab47c2e9a59b4fd1149d8e8fa31ac1a04a9142cda2a479c642fb606eaac14c874fd7426e379f54"
}
]
}
Request information of one or more Virtual Assets Service Provider.
API Endpoint
GET ${sygna-bridge domain}/api/v1/bridge/vasp
Header Values
Parameter | Value | Description |
---|---|---|
api_key |
{apikey} |
replace {apikey} with 64 chars long API key got from Sygna Bridge. |
GET Status
const transfer_id: "eeac79bb6ad673bfb4444b3bed1191c4b084270445becb7fdc2af7a80bb66aab"
const url = "https://staging-api.sygna.io/sb/api/v1/bridge/transaction/status?transfer_id="+transfer_id
const headers = {
"api_key":"4973a45555f3200b34937b5ae27c0da8653a584271fda1869262fc7edad8b88d"
};
let response = await fetch(url, {
method: 'GET',
headers: headers
});
await response = await response.json();
Sample response
{
"transfer_id": "eeac79bb6ad673bfb4444b3bed1191c4b084270445becb7fdc2af7a80bb66aab",
"private_info" : "04f76bf0372c4d4679a172aaaf7fe4746cb83f2a2e5d6a5afcfe6bba72ad4c540d7940853f5fd1f4928c541c39efae81e9165598bad256fab77f6d5c4bd8d135b0553e61bbaeb6d573c71b23d656ed9e4ac37f40106ebd3aab8839c77409a3890b5f056d4e11aaf87c6d29580869ec5f81d20ecaad266ba69e234a2eb627d5c7b95957d82fd1b69172f6bce480f74d9f4ea7d4d38300669c25723e91c5bce83ec8f29aedd4",
"transaction" : {
"originator_vasp_code":"VASPUSNY1",
"originator_addr":"3KvJ1uHPShhEAWyqsBEzhfXyeh1TXKAd7D",
"beneficiary_vasp_code":"VASPUSNY2",
"beneficiary_addr":"3AGUBzgKsZYNjEb3CvYzBS2wHYec4QBuCZ",
"transaction_currency":"0x80000000",
"amount": 0.269726
},
"transfer_id" : "eeac79bb6ad673bfb4444b3bed1191c4b084270445becb7fdc2af7a80bb66aab",
"created_at" : "2019-07-25T09:13:12.557+0000",
"permission_status" : "ACCEPTED",
"transfer_to_sender_time" : "2019-07-25T09:14:04.706+0000",
"txid" : "1a0c9bef489a136f7e05671f7f7fada2b9d96ac9f44598e1bcaa4779ac564dcd"
}
Get status of specific Permission Request.
API Endpoint
GET ${sygna-bridge domain}/api/v1/bridge/transaction/status
Header Values
Parameter | Value | Description |
---|---|---|
api_key |
{apikey} |
replace {apikey} with 64 chars long API key got from Sygna Bridge. |
Request Parameter
Parameter | Type | Description |
---|---|---|
transfer_id |
string | Unique transfer Id got from permissionRequest api call. |
POST Permission Request
const params = {
data: {
private_info: "04857fecaef8ae2b6d1e6555e55c395dd75e507ecec62742f6ac830a0e87b7bbef4c1a15ea049bae9ca925a517863d634933d6fe3099aa72eb4633ee3796f348195932b3647b32841b3535cdea8ddea70a2741dcbf7a9fca3f4660a7d400842d8a9fc4b3667b1b2b3557e004de654c42521166b3056effa77c613670bb2d72286b3c800ff5ce8b8c2274e2b0901797dea36c39eb810b5d5f7709f37c16ed65f3f3d79e4177ac1ed2827073673a9c3d9467504f72b69e7fa6f55b6267f23aca248687435181",
transaction:
{
originator_vasp_code: 'VASPUSNY1',
originator_addr: '3KvJ1uHPShhEAWyqsBEzhfXyeh1TXKAd7D',
beneficiary_vasp_code: 'VASPUSNY2',
beneficiary_addr: '3AGUBzgKsZYNjEb3CvYzBS2wHYec4QBuCZ',
transaction_currency: '0x80000000',
amount: 0.1384
},
data_dt:'2019-07-29T06:29:00.123Z',
signature:"7e440ed0a978d05863d9b5fd674f4dbba264a1ce5fed72cd9bb2bfb16b4deaba54152d2b61f59d0f1ebf7e16fd6f575260851e9f5a1496e8f0f0805faa2f277a",
},
callback: {
callback_url:"https://originator.com/api/v1/originator/transaction/permission",
signature:"86890ed0a978d05863d9b5fd674f4dbba264a1ce5fed72cd9bb2bfb16b4deaba54152d2b61f59d0f1ebf7e16fd6f575260851e9f5a1496e8f0f0805faa553322"
};
};
const url = "https://staging-api.sygna.io/sb/api/v1/bridge/transaction/permission-request"
const headers = {
"Content-Type":"application/json",
"api_key":"4973a45555f3200b34937b5ae27c0da8653a584271fda1869262fc7edad8b88d"
};
let response = await fetch(url, {
method: 'POST',
body: JSON.stringify(params),
headers: headers
});
await response = await response.json();
Sample response
{
"transfer_id":"eeac79bb6ad673bfb4444b3bed1191c4b084270445becb7fdc2af7a80bb66aab"
}
The first step of the permission request flow. Should be called by the originator VASP to inform Sygna Bridge about the creation of a compliant transaction.
API Endpoint
POST ${sygna-bridge domain}/api/v1/bridge/transaction/permission-request
Header Values
Parameter | Value | Description |
---|---|---|
Content-Type |
application/json |
|
api_key |
{apikey} |
replace {apikey} with 64 chars long API key got from Sygna Bridge. |
Body Parameter
Parameter | Type | Description |
---|---|---|
data |
Permission Request Data | |
callback |
Callback | The endpoint url for Sygna Bridge to post final permission. |
POST Permission
const params = {
transfer_id: "eeac79bb6ad673bfb4444b3bed1191c4b084270445becb7fdc2af7a80bb66aab",
permission_status: "ACCEPTED",
signature:"7e440ed0a978d05863d9b5fd674f4dbba264a1ce5fed72cd9bb2bfb16b4deaba54152d2b61f59d0f1ebf7e16fd6f575260851e9f5a1496e8f0f0805faa2f277a",
};
const url = "https://staging-api.sygna.io/sb/api/v1/bridge/transaction/permission"
const headers = {
"Content-Type":"application/json",
"api_key":"4973a45555f3200b34937b5ae27c0da8653a584271fda1869262fc7edad8b88d"
};
let response = await fetch(url, {
method: 'POST',
body: JSON.stringify(params),
headers: headers
});
await response = await response.json();
Sample Response
{
"message":""
}
This request works as a response of Sygna Bridge's Permission Request. Whenever a beneficiary VASP finishes processing the request from Sygna Bridge, it must post a permission
request back to Sygna Bridge server, containing the confirmation result.
API Endpoint
POST ${sygna bridge domain}/api/v1/bridge/transaction/permission
Header Values
Parameter | Value | Description |
---|---|---|
Content-Type |
application/json |
|
api_key |
{apikey} |
replace {apikey} with 64 chars long API key got from Sygna Bridge. |
Body Parameter
Parameter | Type | Description |
---|---|---|
transfer_id |
string | |
permission_status |
string | "ACCEPTED" or "REJECTED" |
signature |
string | signature of object containing previous 2 fields. |
POST Transaction ID
const params = {
transfer_id: "eeac79bb6ad673bfb4444b3bed1191c4b084270445becb7fdc2af7a80bb66aab",
txid:"1a0c9bef489a136f7e05671f7f7fada2b9d96ac9f44598e1bcaa4779ac564dcd",
signature:"7e440ed0a978d05863d9b5fd674f4dbba264a1ce5fed72cd9bb2bfb16b4deaba54152d2b61f59d0f1ebf7e16fd6f575260851e9f5a1496e8f0f0805faa2f277a"
};
const url = "https://staging-api.sygna.io/sb/api/v1/bridge/transaction/txid"
const headers = {
"Content-Type":"application/json",
"api_key":"4973a45555f3200b34937b5ae27c0da8653a584271fda1869262fc7edad8b88d"
};
let response = await fetch(url, {
method: 'POST',
body: JSON.stringify(params),
headers: headers
});
await response = await response.json();
Sample response
{
"message": ""
}
Submit broadcasted transaction ID on the blockchain for Sygna Bridge to keep as a record.
API Endpoint
POST ${sygna-bridge domain}/api/v1/bridge/transaction/txid
Header Values
Parameter | Value | Description |
---|---|---|
Content-Type |
application/json |
|
api_key |
{apikey} |
replace {apikey} with 64 chars long API key got from Sygna Bridge. |
Body Parameter
Parameter | Type | Description |
---|---|---|
transfer_id |
string | Unique transfer Id got from permissionRequest api call. |
txid |
string | Corresponding hex transaction ID on the blockchain |
signature |
string | The signature of object containing 2 previous field. |
VASP API
A Vasp must implement the following 2 endpoints to fully function with the Sygna Bridge ecosystem, acting as a beneficiary and an originator accordingly.
We have provided a sample NodeJS based API on Github so you can take a look at how the two endpoints should process or callback Sygna Bridge.
POST Permission Request (as Beneficiary)
Your beneficiary server should be able to response to following request.
const params = {
data: {
private_info: "04857fecaef8ae2b6d1e6555e55c395dd75e507ecec62742f6ac830a0e87b7bbef4c1a15ea049bae9ca925a517863d634933d6fe3099aa72eb4633ee3796f348195932b3647b32841b3535cdea8ddea70a2741dcbf7a9fca3f4660a7d400842d8a9fc4b3667b1b2b3557e004de654c42521166b3056effa77c613670bb2d72286b3c800ff5ce8b8c2274e2b0901797dea36c39eb810b5d5f7709f37c16ed65f3f3d79e4177ac1ed2827073673a9c3d9467504f72b69e7fa6f55b6267f23aca248687435181",
transaction:
{
originator_vasp_code: 'VASPUSNY1',
originator_addr: '3KvJ1uHPShhEAWyqsBEzhfXyeh1TXKAd7D',
beneficiary_vasp_code: 'VASPUSNY2',
beneficiary_addr: '3AGUBzgKsZYNjEb3CvYzBS2wHYec4QBuCZ',
transaction_currency: '0x80000000',
amount: 0.1384
},
data_dt:'2019-07-29T06:29:00.123Z',
signature:"7e440ed0a978d05863d9b5fd674f4dbba264a1ce5fed72cd9bb2bfb16b4deaba54152d2b61f59d0f1ebf7e16fd6f575260851e9f5a1496e8f0f0805faa2f277a",
},
transfer_id:"eeac79bb6ad673bfb4444b3bed1191c4b084270445becb7fdc2af7a80bb66aab"
};
const url = "https://beneficiary.example/api/v1/bridge/transaction/permission-request"
const headers = {
"Content-Type":"application/json",
};
let response = await fetch(url, {
method: 'POST',
body: JSON.stringify(params),
headers: headers
});
await response = await response.json();
Sample Response
{
"message":""
}
When the Sygna Bridge get a Permission Request, it will verify the request first to ensure the data integrity, and then Relay the request to corresponding beneficiary server.
API Endpoint
POST ${beneficiary domain}/api/v1/beneficiary/transaction/permission-request
Header Values
Parameter | Value | Description |
---|---|---|
Content-Type |
application/json |
Body Parameter
Most of the body parameter
Parameter | Type | Description |
---|---|---|
data |
Permission Request Data | Data from originator |
transfer_id |
string |
POST Permission (as Originator)
Your originator server should be able to response to following request.
const params = {
transfer_id: "eeac79bb6ad673bfb4444b3bed1191c4b084270445becb7fdc2af7a80bb66aab",
permission_status: "ACCEPTED",
signature:"7e440ed0a978d05863d9b5fd674f4dbba264a1ce5fed72cd9bb2bfb16b4deaba54152d2b61f59d0f1ebf7e16fd6f575260851e9f5a1496e8f0f0805faa2f277a",
};
const url = "https://originator.example/api/v1/originator/transaction/permission"
const headers = {
"Content-Type":"application/json",
};
let response = await fetch(url, {
method: 'POST',
body: JSON.stringify(params),
headers: headers
});
await response = await response.json();
Each VASP needs to implement a permission
endpoint to get a notification from Sygna Bridge.
When an originator posts a permission request to Sygna Bridge, it needs to attach this url in callback_url
.
API Endpoint
POST ${originator domain}/api/v1/originator/transaction/permission
Header Values
Parameter | Value | Description |
---|---|---|
Content-Type |
application/json |
Parameter | Type | Description |
---|---|---|
transfer_id |
string | Data from Beneficiary |
permission_status |
string | Data from Beneficiary |
signature |
string | Data from Beneficiary |
Custom Objects
Permission Request Data
Sample Transansfer Data Object
{
"private_info": "04857fecaef8ae2b6d1e6555e55c395dd75e507ecec62742f6ac830a0e87b7bbef4c1a15ea049bae9ca925a517863d634933d6fe3099aa72eb4633ee3796f348195932b3647b32841b3535cdea8ddea70a2741dcbf7a9fca3f4660a7d400842d8a9fc4b3667b1b2b3557e004de654c42521166b3056effa77c613670bb2d72286b3c800ff5ce8b8c2274e2b0901797dea36c39eb810b5d5f7709f37c16ed65f3f3d79e4177ac1ed2827073673a9c3d9467504f72b69e7fa6f55b6267f23aca248687435181",
"transaction":
{
"originator_vasp_code":"VASPUSNY1",
"originator_addr":"3KvJ1uHPShhEAWyqsBEzhfXyeh1TXKAd7D",
"beneficiary_vasp_code":"VASPUSNY2",
"beneficiary_addr":"3AGUBzgKsZYNjEb3CvYzBS2wHYec4QBuCZ",
"transaction_currency":"0x80000000",
"amount": 0.97384
},
"data_dt":"2019-07-29T06:29:00.123Z",
"signature":"3044022007d96bd01bcbcb11149a8871370617c840a74a09bb6b66da5681748b6a0b58f902203de2977c12609761d91cbcd769d8f6e7315d33bbe0c6220d5428d9c2d5c5a8ab"
}
Key | Type | Description |
---|---|---|
private_info |
string | ECIES encoded Private Information |
transaction |
Transaction | |
data_dt |
string | Timestamp in ISO format: yyyy-mm-ddThh:mm:ss[.mmm]+0000 |
signature |
string |
Transaction
Sample Transaction Object
{
"originator_vasp_code":"VASPUSNY1",
"originator_addr":"3KvJ1uHPShhEAWyqsBEzhfXyeh1TXKAd7D",
"beneficiary_vasp_code":"VASPUSNY2",
"beneficiary_addr":"3AGUBzgKsZYNjEb3CvYzBS2wHYec4QBuCZ",
"transaction_currency":"0x80000000",
"amount": 0.97384
}
Key | Type | Description |
---|---|---|
originator_vasp_code |
string | |
originator_addr |
string | |
beneficiary_vasp_code |
string | |
beneficiary_addr |
string | |
transaction_currency |
string | ID format specified in BIP-44 |
amount |
number |
Callback
Sample Originator Callback Object
{
"callback_url":"https://originator/api/v1/originator/transaction/permission",
"signature":"376f567dc97ce1132181b962e082423ffc98c6379603aab2f645f30cae11e5483252333f2c383d6c190638f0f26e2476c7a2d6e17fa3c55521b04f00e4be05b4",
}
Key | Type | Description |
---|---|---|
callbck_url |
string | |
signature |
string |
Private Information
Sample Private Information Object
{
"originator": {
"name": "David Beckham",
"date_of_birth":"1975-05-02"
},
"beneficiary":{
"name": "Antoine Griezmann"
}
}
Private information is the object that is being encoded with ECIES.
Key | Type | Description |
---|---|---|
originator |
OriginatorInfo | |
beneficiary |
BeneficiaryInfo |
OriginatorInfo
Key | Type | Description |
---|---|---|
name |
string | Use native language characters for local transactions; use romanized names for international transactions |
physical_address (optional) |
string | Physical Address ( For originator from US VASP ) |
national_identity_number (optional) |
string | National Identity Number |
date_of_birth (optional) |
string | Date of birth in ISO format |
unique_identity (optional) |
string |
BeneficiaryInfo
Key | Type | Description |
---|---|---|
name |
string | Use native language characters for local transactions; use romanized names for international transactions |
Errors
The API uses the following error codes:
HTTP Status | Meaning |
---|---|
400 | Bad Request -- Your request is malformed, invalid, or you are missing parameters |
401 | Unauthorized -- Please make sure authentication was done properly |
403 | Forbidden -- This API request requires additional access rights. |
404 | Not Found -- The specified entity could not be found. |
500 | The server encountered an unexpected condition which prevented it from fulfilling the request. |