Links
🪝

Webhook

To configure Backup and Recovery with Portal, you need to configure a webhook.

Why do I need a webhook?

The custodian backup share is stored on your servers.
During the backup process, Portal generates the custodian backup share on its servers and then sends it to your servers via a webhook request for you to store. After the webhook request completes, the custodian backup share is deleted from the Portal servers. Portal never stores the custodian backup share in its database, it is only ever in memory during the duration of the backup process.
During the recovery process, Portal requests a user's custodian backup share from your backend via a webhook request. After the wallet is successfully recovered, new pairs of signing and backup shares are generated resulting the backup processing twice more.

API Endpoints you need to build

There are two webhook routes that your server needs to support: /backup for Backup and /backup/fetch for Recovery.

Backup

During the backup process the Portal backend sends the custodian backup share to your API to store. Portal sends a POST request to <WebhookBaseUrl>/backup.
POST https://yourserver.com/backup
// Request headers:
{
"Accept": "application/json",
"Content-Type": "application/json",
"X-Webhook-Secret": "webhookSecretHere"
}
// Request body:
{
"clientId": "clientIdHere",
"share": "backupShareForClientId"
}
Your webhook server is expected to store the clientId and share value in a secure database.
The backup share is critical to protecting your users' wallet. It is important to treat the share value as a sensitive value and handle it accordingly. Do not log the value (or request body), be sure to store the value encrypted at rest, and understand the access control on the database.
It should also return a 200 status code.
Here is a simple example of how to set this up using Node and Express:
const app = express()
app.post('/backup', async (req, res) => {
// Store backup share in your database.
await db.user.update({
where: {
clientId: req.body.clientId,
},
data: {
backupShare: req.body.share,
},
})
res.status(200).send()
})

Recovery

During the recovery process the Portal backend requests the custodian backup share from your API. Portal sends a POST request to <WebhookBaseUrl>/backup/fetch.
POST https://yourserver.com/backup/fetch
// Request headers:
{
"Accept": "application/json",
"Content-Type": "application/json",
"X-Webhook-Secret": "webhookSecretHere"
}
// Request body:
{
"clientId": "clientIdHere",
}
Your webhook server is expected to fetch the backup share for the given clientId.
It should also return a 200 status code along with the backupShare in the response body.
// Resonse body:
{
"backupShare": "backupShareForClientId"
}
Here is a simple example of how to set this up using Node and Express:
// example webhook server (express)
const app = express()
app.post('/backup/fetch', async (req, res) => {
// Find the user by clientId.
const user = await this.db.user.find({
where: {
clientId: req.body.clientId,
}
})
res.status(200).json({ backupShare: user.backupShare })
})

Configuration

To configure your webhook go to the Settings Page of the Portal web app. Navigate to the Webhooks section and select the "New +" button. Enter in the base URL for your webhook server and select "Create". A secure, random webhook secret will be automatically generated for you unless you prefer to create your own secret.
Setting up the webhook base URL.
Once the webhook base URL has been added. You will be able to view the webhook configuration. See the image below describing the different components of the webhook configuration.
  1. 1.
    Base URL - This is the URL you've configured.
    1. 1.
      When Portal runs backup it will send a request to the [baseUrl]/backup route to send your server a backup share to save for a client.
    2. 2.
      When Portal runs recover it will send a request to the [baseUrl]/backup/fetch route to request a backup share from your server.
  2. 2.
    Webhook Secret - Click the icon to reveal your webhook secret.
  3. 3.
    Webhook Route - Each route that Portal sends requests to has a status indicator letting you know whether the route is working correctly.
  4. 4.
    Request Explorer - To view the HTTP requests and responses sent to each route select "View Requests".

Security

To ensure secure transmission of recovery shares via the webhook we follow several security best practices.

Webhook Secret

Including a secret along with each webhook request adds authentication to the requests sent to your webhook server. You should verify that the secret with the request matches the secret configured in Portal.
All of our webhook requests are sent with a secret in the X-Webhook-Secret header. The secret is securely generated on webhook configuration and can be viewed in the Settings Page of the web app.
A configured webhook in the Settings page of the web app.

IP Allowlist

Restricting requests on your webhook server to only those from Portal's IP addresses protects against requests from other parties. Configure your webhook server to only accept inbound connections from Portal's IP address.
Portal always makes requests from the IP address 35.203.150.117.