Skip to main content
Flow control hooks are requests Portal makes to your server in the middle of a wallet operation. Portal waits for your response, and the response decides what happens: a signature goes through or is rejected, a backup is stored or fails, a recovery gets its share or fails. You configure one base URL, and Portal appends a fixed path for each hook: Signature approvals and self-managed backups are turned on and off separately. To create the configuration, follow Set up flow control hooks.
Signature approvals used to be the PRE_SIGN_V1 informational webhook event. Existing PRE_SIGN_V1 subscriptions were moved to flow control automatically. If you were subscribed to PRE_SIGN_V1, your endpoint keeps working as before: Portal still calls the same URL with the same request body, the same X-WEBHOOK-SECRET header, and the same secret.

Request headers

Every flow control hook request includes these headers: All hooks use the same secret, unless you used PRE_SIGN_V1 before. In that case /sign-approval keeps the secret it had and the backup hooks use a different one, so check each path against its own secret. See Secrets.
X-Webhook-Secret carries the secret itself. Portal does not sign the request body. Compare the header to your stored secret on every request, and reject requests that don’t match.

Signature approvals

When signature approvals are on, Portal calls POST [baseUrl]/sign-approval before every signature any of your clients makes, and holds the signature until your server answers.

Request body

Response

Portal only reads the status code. The response body is ignored. Portal makes one attempt per signature and doesn’t retry. If your server is down, every signature fails until it comes back or you turn signature approvals off.

Self-managed backups

By default, Portal stores the custodian backup share for each of your clients. With self-managed backups, Portal sends that share to your server instead, and asks for it back when a client recovers. Portal only holds the share in memory while a backup or recovery runs. If your environment uses Portal-managed backups, the dashboard hides these hooks and you don’t need to build them.

POST [baseUrl]/backup

Portal calls /backup when a client backs up a wallet. The request body has three fields: Store the share by clientId and backupMethod, replacing any share you already hold for that pair, then respond with a 2xx status such as 200. Any other response, or no response within 10 seconds, fails the backup. Right after a successful /backup call, Portal calls /backup/fetch for the same client to check that the share was stored. If the share Portal gets back doesn’t match the one it sent, the backup fails.
Store every share Portal sends you, even for curves you don’t use yet. If you only support Solana and receive both GDRIVE-SECP256K1 and GDRIVE-ED25519, store both. Recovery needs all of them.
The backup share protects your clients’ wallets. Don’t log it or the request body, encrypt it at rest, and restrict who can read the table it lives in.

POST [baseUrl]/backup/fetch

Portal calls /backup/fetch when a client recovers a wallet, and right after every /backup call. The request body has one field, clientId. Respond with 200 and every custodian backup share you hold for that client, exactly as you received them in /backup:
Portal picks the share whose backupSharePairId matches the backup the client is recovering from. A single share in a backupShare string field also works, but backupShares lets Portal choose. If neither field is present, or no share matches, the recovery fails.

Example database model

With self-managed backups you store two kinds of share, both unique by client and backup method:
  • The custodian backup share, which Portal sends to /backup.
  • The encrypted client backup share (cipherText), which your app receives from the SDK’s backup method and sends to your backend. Your app fetches it again before calling the SDK’s recover method.
How your app stores the encrypted client backup share depends on the platform. See the backup guide for your SDK, for example Back up a wallet.

Example server

This Express server handles all three hooks and checks the secret header on every request:
Replace isAllowed with your own approval rules. BACKUP_SHARE_ENCRYPTION_KEY is a base64-encoded 32-byte key. In production, keep it in a key management service rather than an environment variable.

Recognizing test calls

The dashboard can send a test call to each hook. Test calls use the same headers and body shape as real calls, and the request body has no field that marks them as tests. You can recognize them by these values:
A test call to /backup reaches your real backup storage with a fake share for a fake client. Store it or ignore it, but respond with a 2xx so the test passes. The share can’t recover any wallet.
Test calls show up in the flow control event log with a Test badge and don’t count toward the 24-hour stats or hook status.

Manage in the dashboard

Open Webhooks > Flow control in the Portal Admin Dashboard. The page shows:
  • Calls · 24h, Approved, Rejected, and Failed: counts for the last 24 hours. Approved and Rejected only cover signature approvals. Failed includes errors and timeouts.
  • Flow control event log: every call Portal made and the exact response your server returned.
  • Hooks: each hook with its path, status, number of calls in the last 24 hours, and time of the last call.
  • Base URL, your secrets, and the Email alerts setting.

Hook status

A 400 from /sign-approval is a rejection, not a failure.

Turn hooks on or off

Check or uncheck Signature approvals or Self-managed backups in the Hooks list, then click Save changes and confirm. Turning a hook off has these effects:
  • Signature approvals off: Portal stops calling /sign-approval, and signatures proceed without your approval.
  • Self-managed backups off: Portal stops calling /backup and /backup/fetch, and backups and recoveries fail until you turn them back on.
The base URL can’t be changed from the dashboard after setup. Contact our team if you need to move it.

Test your hooks

Click Test all hooks at the top of the page, or Test on a single hook. Portal sends a test call and shows the status code your server returned, the response time, and the response body. Click Show test request body on a hook to see what Portal sends.

Secrets

Click the reveal icon next to a secret to see it. Revealing a secret requires two-factor authentication. When /sign-approval and the backup hooks use different secrets, the page shows two: Sign approval secret for /sign-approval and Backup secret for /backup and /backup/fetch.

Email alerts

Turn on Email alerts to get an email when a hook keeps failing. Portal sends at most one failure email per hook per hour, and a recovery email once the hook works again.

Flow control events

Click View flow control events to see every call Portal made to your base URL in the last 30 days. Filter by Hook and by date range with From and To. Each row shows the hook, the status code, the outcome, and when Portal received the response. Expand a row to see the request body and your server’s response. Portal hides backup shares and the secret header in this log.

Security

  • Base URLs must use HTTPS.
  • Check the X-Webhook-Secret header on every request.
  • Only accept requests from Portal’s IP addresses: 35.203.150.117, 104.155.171.139, and 35.185.20.23.