API Reference

Settings

⚙️

Webhook URL settings

The target URL to which HTTP POST requests will be sent needs to be provided to your integration manager, and confirmation should be received that it has been added to the partner settings on Paybis end.

Available webhooks

1. Verification Status Update

This webhook is called for each case when the user initiates/completes SumSub KYC verification in PnP wallets application, when crypto transaction is initiated.

Webhook messages are sent on the following status update events:

  • started - user initiated the KYC verification process but the final application status hasn't been yet received from SumSub OR RETRY result is returned (the user is supposed to resubmit the requested documents).
  • approved - KYC application was approved (GREEN result is received from SumSub).
  • failed - KYC application was rejected (RED final reject is received from SumSub).

Event parameters:

ParameterDescription
partnerUserIdThe customer's unique ID in the partner's system used in the POST Request endpoint.
statusKYC verification status of the customer with corresponding partnerUserId. Possible values: Started, Approved, Failed.
timestampThe time when the KYC verification status was updated in Unix 10-digit epoch time format.
residenceCountryTwo-letter ISO code of the country where user was verified.

Example webhook payload with verification-started status:

{
  "event": "VERIFICATION_STATUS_UPDATED",
  "data": {
    "partnerUserId":"e18fb964-fd9a-4de7-96c4-u1dq8a1ddd1",
    "status":"started"
  },
  "timestamp":1653593988
}

Example webhook payload with basic-verification-failed status:

If the verification has been failed, the residenceCountry parameter is set to null.

{
  "event": "VERIFICATION_STATUS_UPDATED",
  "data": {
    "partnerUserId":"e18fb964-fd9a-4de7-96c4-u1dq8a1ddd1",
    "status":"failed",
    "residenceCountry": null
  },
  "timestamp":1653593988
}

Example webhook payload with basic-verification-approved status:

Additional parameter residenceCountry is set to the country of the verification.

{
  "event": "VERIFICATION_STATUS_UPDATED",
  "data": {
    "partnerUserId":"e18fb964-fd9a-4de7-96c4-u1dq8a1ddd1",
    "status":"approved",
    "residenceCountry": "LV"
  },
  "timestamp":1653593988
}

📘

Shared access token

If you are using the Shared KYC solution, you can call the v1/sumsub/shared-token endpoint to retrieve the Shared Access Token for approved applicants. Shared token can be used to import applicant´s KYC data from SumSub.

2. Crypto Transaction Status

This webhook provides real-time updates on the status of cryptocurrency checkout transactions within the Plug'n'Play Wallets flow. The webhook is executed using HTTP POST method and contains JSON data with a CRYPTO_CHECKOUT_TRANSACTION_CHANGED event inside.

Webhook messages are sent on the following status update events:

  • confirming - transaction is being confirmed on the blockchain, and the required number of confirmations is not yet reached.
  • completed - transaction has been successfully completed, and the funds have been transferred to the designated wallet.
  • canceled - transaction has been canceled by the user or the system.
  • failed - transaction has failed due to an error, such as insufficient funds or network issues.

Webhook Structure

The webhook is sent via HTTP POST and contains a JSON payload with the following structure:

{
    "event": "CRYPTO_CHECKOUT_TRANSACTION_CHANGED",
    "data": {
        "cryptoCheckoutId": "9f6e6fb2-e1c7-4aa6-828c-f7c48df2a457",
        "transaction": {
            "hash": "047489836f7cbc7a5e41120bbe9a8007cac6179ab3108d673da7ff15b20aefee",
            "status": "completed",
            "fromAddress": {
                "address": "TKzB2CW95MLpP8yHXvKoQsAJjiyGDX4jDw"
            },
            "toAddress": {
                "address": "TCd9qHyjqiUkfTxe3gotbuTMpju8LEbpkN"
            },
            "explorerLink": "https://blockchain.list",
            "assetId": "USDT-TRC20",
            "amount": {
                "amount": "200.00",
                "currency": "USDT"
            },
            "networkFee": {
                "amount": "4.00",
                "currency": "USD"
            },
            "createdAt": "2024-05-29T14:52:42+00:00"
        }
    },
    "timestamp": 1718620483
}

Event parameters:

ParameterDescription
cryptoCheckoutIdThe unique identifier of the checkout session.
transaction.hash Unique identifier of the transaction on the blockchain (available only for completed and confirming statuses).
transaction.status Current status of the transaction (confirming, completed, canceled, or failed).
transaction.fromAddress.address The address from which the cryptocurrency was sent.
transaction.toAddress.address The address to which the cryptocurrency was sent.
transaction.explorerLink A link to the block explorer where the transaction details can be viewed.
transaction.assetId The identifier of the crypto asset used in the transaction (e.g., USDT-TRC20).
transaction.amount.amount The amount of cryptocurrency involved in the transaction.
transaction.amount.currencyThe cryptocurrency used in the transaction (e.g., USDT).
transaction.networkFee.amountThe amount of network fee charged for the transaction.
transaction.networkFee.currency The currency in which the network fee was charged (e.g., USD).
transaction.createdAtThe date and time when the transaction was created in ISO 8601 format.
timestampThe time when the webhook message was sent in Unix 10-digit epoch time format.

Retry policy

The HTTP 2xx responses indicate that our webhook call was successful. If your server returns any other response, we retry up to 80 times with the exponential backoff (backoff multiplier = 2). The first retry is made in 10 sec after unsuccessful response. The maximum delay between the retries is limited to 6 h: when this limit is reached, multiplier no longer applies.

Security

For your safety we sign each webhook message with the 4096 bit RSA key using the RSASSA_PSS_SHA_512 asymmetric signing algorithm. The signature is encoded to base64 and sent with the original request in the X-Request-Signature header.

Use our public key to verify incoming POST calls.

Production public key:

-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv0zlJaY8HC+39L8yacXD
sOz/zGMPWmu3uLj3+rBGsyRk8UHLCtLM7m9Zp7CXNPeN0elJ67R7fIfYzz5j0R9M
f8MPhok4H72eO/gHga+wTuLBz0VpTGWLykVPPM+R+fv0IJW0J3DBaUWo8iYgd62F
SUQwutXFPKGA67zSM7MvKtBdzgE6f2bb6O6XCg8tWyOqHLROGl8T5rAQphUW6UQc
MxO88jAwL64n9Xb0+H6XBtLwlUc/xJhb18Ag4T4OCvdyJU0TT849EJxJb1hGTjHP
ml6bSowhkjIXouwTpqES7MPaVAWmwE4YzS7jBeNiP1wvoa6u0p2esOVIj/9daKDL
He5soYOrq7z6TKWphqW57NI5YHHQ1Mo/W7OezfiZQNueBSv+f9ynF3SlfF5xB+6T
+3xP6MTYwugdB7PMam3J4klwsoeAb6sLlsHbjM2vk0ji2OkZkiv8iPp/eeD52UT2
SFcNmPKY5fYOU+31WSqwIWc4bb8UYzkBrDAFEXcNtOf6w36ma+dnyqhxZpW6ltnf
/gjSEd/nsO/HEG15pbbL8AlX7W9uK5ea4D8uLKWHWzfcVlT3ZLT0/YVKy+sfpC2h
mmxaoKEiHt9OiTK9+zbBsTD4FAtRq0T7EIoCoJCd+8OoOQz4x4p+VTaDi9mAbFd/
8D6LwL35KLfRkkeHsUCnNlkCAwEAAQ==
-----END PUBLIC KEY-----

Sandbox environment public key:

-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApR7LLr506sbJjzs5BuBf
Ubu5Efi+fFN3XlGx6wrKimZ5OSSImHOA8T5fRy/teRriD/92+V18pAh6jmMR7r9E
nnIskJ45IwlMbrp6HRQ5GGt2phHxwj31MvkB+JahqDZrJ6GCwWSd/i7gZjizLy03
pxzV1Sw02342pQMtHX8QgwV5j3/J8Btez5bANHZn5Zp9FS9N6pkedOiZWjiSWOFQ
YUk73VhyW5TjXN5MYQ6FlHmPdwm/Qe/x4DZYXLNAMlFL8Tsb3xNkekJiJPKyr0h2
vqmbEdc9WYtaJAilVS6Yt0QOJtymmQsowCbP7mUFW/i7q8ayjrRUyLnzmoR8H+yY
G+B8lcpu7Aqt0lxUTMRm5KwnTkUyZrimwReWE8LVc68Ae7t4Qxj1dN6nLegDWO7G
BynD7D8ESJ6bNp6GCbc5ntY1T5g+HIGrff7DclcYfzu6RNVgKFlnLxue9J6iJv8q
4wFtn3OM3hxDG/SDk+YUlXiVeUNjPjoA8Z4aEE7OkJBouykLVSiHn4nVrN0WZ1+y
ouYyGwFbL2Vw5G4QR+bi3CZP6rYk9X3A8/xzXjDSYoAqK1+0/7Qncmapbr1Id8qc
huUr+tJq91Ua+EjpdjfaxOrSVBts0iYujY0ahrVCFYBlqu89MSOW4tM4BEgkOeN/
IrZj8Jj85onbaoJr1svCpZUCAwEAAQ==
-----END PUBLIC KEY-----

Signature validation example: node.js

const crypto = require("crypto");

const publicKey = `-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApR7LLr506sbJjzs5BuBf
Ubu5Efi+fFN3XlGx6wrKimZ5OSSImHOA8T5fRy/teRriD/92+V18pAh6jmMR7r9E
nnIskJ45IwlMbrp6HRQ5GGt2phHxwj31MvkB+JahqDZrJ6GCwWSd/i7gZjizLy03
pxzV1Sw02342pQMtHX8QgwV5j3/J8Btez5bANHZn5Zp9FS9N6pkedOiZWjiSWOFQ
YUk73VhyW5TjXN5MYQ6FlHmPdwm/Qe/x4DZYXLNAMlFL8Tsb3xNkekJiJPKyr0h2
vqmbEdc9WYtaJAilVS6Yt0QOJtymmQsowCbP7mUFW/i7q8ayjrRUyLnzmoR8H+yY
G+B8lcpu7Aqt0lxUTMRm5KwnTkUyZrimwReWE8LVc68Ae7t4Qxj1dN6nLegDWO7G
BynD7D8ESJ6bNp6GCbc5ntY1T5g+HIGrff7DclcYfzu6RNVgKFlnLxue9J6iJv8q
4wFtn3OM3hxDG/SDk+YUlXiVeUNjPjoA8Z4aEE7OkJBouykLVSiHn4nVrN0WZ1+y
ouYyGwFbL2Vw5G4QR+bi3CZP6rYk9X3A8/xzXjDSYoAqK1+0/7Qncmapbr1Id8qc
huUr+tJq91Ua+EjpdjfaxOrSVBts0iYujY0ahrVCFYBlqu89MSOW4tM4BEgkOeN/
IrZj8Jj85onbaoJr1svCpZUCAwEAAQ==
-----END PUBLIC KEY-----`;

const message = '{"event":"VERIFICATION_STATUS_UPDATED","data":{"partnerUserId":"e18fb964-fd9a-4de7-96c4-1lclszzd","status":"started"},"timestamp":1654073212}'
var signature = 'jcbM5ysMZuQnUplcBOTqOiPQ3jOsIgl+imMy+AJtbTlvYG3c62WKyYTtm9BvBSuplEvhxOKoe9RBHjYkYsvaOelZr+RcnHkhg1EzEVXfaISJDA6fUyi38uGkWihVnzBjkDvZf7mgpY/4a4q3A+7dN2m5yNQPwPlf/HdExeNQTnrLEANB0rHDa5X091ID5E6vZjccF4/52MkrKdW+9DVbcqT7QLfFwaTxO/E90Aal3tGt/HqE+Ybhw+RocNl14elXeWUm8TtEOn7sr23RxskrBJkuVf68k2zD9zdKiPU7bB94ughqQUbz+6zln8W2F5PugIDkRFbrcigesL1xuR0fRfZJPmqRkaRyy6bZ4mNAisQAo+lBwzQOmowuT+8c/UgS3xU6KT1UF7+W8fj1J4JQ//TjMwWKkYDSpx8fAMGs458jh4+FmrnzFwE9XwXeTDPf38OeREZ7e7AHptyEKfZojjkVbdYtHrQjdqHdkXT35JF23uuItxMEEIEh6jy4STdjHcgl5e1V2N9QNg6V/0Cqax5at60I6oek8iMLgBLnSVOh8DNBSj5Fdt8fQ+kLZj6Q/S+9E9PP2L96Abw9qvexNGpSX2ShgQdkDbe/RsbpN+ORHOfPnnvdzXKsYR4iiVD23GNrsh4QgBd5rbwI7KgloCovpdQhjiyV07g/qBAg+kM';

const verified = crypto.verify(
  'sha512',
  Buffer.from(message),
  {
	key: publicKey,
	padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
  },
  Buffer.from(signature, 'base64'),
)

console.log("Verified:", verified);

Signature validation example: PHP

<?php

use phpseclib\Crypt\RSA;

$publicKey = <<<PUBKEY
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEApR7LLr506sbJjzs5BuBf
Ubu5Efi+fFN3XlGx6wrKimZ5OSSImHOA8T5fRy/teRriD/92+V18pAh6jmMR7r9E
nnIskJ45IwlMbrp6HRQ5GGt2phHxwj31MvkB+JahqDZrJ6GCwWSd/i7gZjizLy03
pxzV1Sw02342pQMtHX8QgwV5j3/J8Btez5bANHZn5Zp9FS9N6pkedOiZWjiSWOFQ
YUk73VhyW5TjXN5MYQ6FlHmPdwm/Qe/x4DZYXLNAMlFL8Tsb3xNkekJiJPKyr0h2
vqmbEdc9WYtaJAilVS6Yt0QOJtymmQsowCbP7mUFW/i7q8ayjrRUyLnzmoR8H+yY
G+B8lcpu7Aqt0lxUTMRm5KwnTkUyZrimwReWE8LVc68Ae7t4Qxj1dN6nLegDWO7G
BynD7D8ESJ6bNp6GCbc5ntY1T5g+HIGrff7DclcYfzu6RNVgKFlnLxue9J6iJv8q
4wFtn3OM3hxDG/SDk+YUlXiVeUNjPjoA8Z4aEE7OkJBouykLVSiHn4nVrN0WZ1+y
ouYyGwFbL2Vw5G4QR+bi3CZP6rYk9X3A8/xzXjDSYoAqK1+0/7Qncmapbr1Id8qc
huUr+tJq91Ua+EjpdjfaxOrSVBts0iYujY0ahrVCFYBlqu89MSOW4tM4BEgkOeN/
IrZj8Jj85onbaoJr1svCpZUCAwEAAQ==
-----END PUBLIC KEY-----
PUBKEY;
$message = '{"event":"VERIFICATION_STATUS_UPDATED","data":{"partnerUserId":"e18fb964-fd9a-4de7-96c4-1lclszzd","status":"started"},"timestamp":1654073212}';
$signature = 'jcbM5ysMZuQnUplcBOTqOiPQ3jOsIgl+imMy+AJtbTlvYG3c62WKyYTtm9BvBSuplEvhxOKoe9RBHjYkYsvaOelZr+RcnHkhg1EzEVXfaISJDA6fUyi38uGkWihVnzBjkDvZf7mgpY/4a4q3A+7dN2m5yNQPwPlf/HdExeNQTnrLEANB0rHDa5X091ID5E6vZjccF4/52MkrKdW+9DVbcqT7QLfFwaTxO/E90Aal3tGt/HqE+Ybhw+RocNl14elXeWUm8TtEOn7sr23RxskrBJkuVf68k2zD9zdKiPU7bB94ughqQUbz+6zln8W2F5PugIDkRFbrcigesL1xuR0fRfZJPmqRkaRyy6bZ4mNAisQAo+lBwzQOmowuT+8c/UgS3xU6KT1UF7+W8fj1J4JQ//TjMwWKkYDSpx8fAMGs458jh4+FmrnzFwE9XwXeTDPf38OeREZ7e7AHptyEKfZojjkVbdYtHrQjdqHdkXT35JF23uuItxMEEIEh6jy4STdjHcgl5e1V2N9QNg6V/0Cqax5at60I6oek8iMLgBLnSVOh8DNBSj5Fdt8fQ+kLZj6Q/S+9E9PP2L96Abw9qvexNGpSX2ShgQdkDbe/RsbpN+ORHOfPnnvdzXKsYR4iiVD23GNrsh4QgBd5rbwI7KgloCovpdQhjiyV07g/qBAg+kM';

$verifier = new \phpseclib\Crypt\RSA();
$verifier->setSignatureMode(RSA::SIGNATURE_PSS);
$verifier->setHash('sha512');
$verifier->setMGFHash('sha512');
$verifier->loadKey($publicKey);

$valid = $verifier->verify($message, base64_decode($signature));

🚧

Note that you should use version 2.0 of the phpseclib/phpseclib library: phpseclib/phpseclib:^2.