Skip to main content
Webhooks let your system react to events in Portal.io without polling the API continuously. When a subscribed event occurs — such as a proposal being approved or an AI build completing — Portal.io sends an HTTP POST request to your configured endpoint with a JSON payload describing the event.
Use webhook events alongside targeted API calls to keep your system in sync. For example, listen for Proposal Status Changed to trigger a workflow, then call GET /public/proposals/{ProposalId} to fetch the full updated proposal details.

Available events

Portal.io fires webhooks for the following event types: In addition, Portal.io supports the following Zapier trigger events, which fire on the same underlying data changes:

Subscribing to webhooks

Create a webhook subscription by calling POST /public/webhook/subscribe with your endpoint URL and the events you want to receive.
The response includes:
  • subscriptionId — use this to update or delete the subscription later
  • secretKey — use this to verify that incoming webhook requests are genuinely from Portal.io (see Verifying webhook signatures)
  • url, description, enabled, and events
Your endpoint URL must use HTTPS and must be publicly routable. Portal.io rejects private, loopback, and link-local addresses.

Managing subscriptions

What your endpoint receives

When an event fires, Portal.io sends an HTTP POST to your endpoint with:
  • Content-Type: application/json
  • An X-Webhook-Signature header for verification (see below)
  • A JSON body describing the event
Your endpoint must respond with a 2xx HTTP status code to acknowledge receipt. If Portal.io does not receive a 2xx response, it retries the delivery on this schedule:

Verifying webhook signatures

Every webhook request includes an X-Webhook-Signature header so you can confirm the request came from Portal.io and has not been tampered with. Header format:
Where:
  • t — Unix timestamp of when the webhook was generated
  • v1 — HMAC-SHA256 signature
Signature calculation:
For example, if t is 1710000000 and the request body is {"id":10025,"number":4123,...}, the signed message is:
Verification steps:
1

Read the raw request body

Use the raw bytes exactly as received — do not parse or normalize the JSON before verifying.
2

Extract the signature header values

Parse X-Webhook-Signature to extract t and v1.
3

Rebuild the signed message

Concatenate t, a literal ., and the raw request body.
4

Compute the expected signature

Run HMAC_SHA256(secretKey, signedMessage) using your subscription’s secretKey.
5

Compare and validate

Compare your computed value with v1. Reject the request if they do not match, or if t is more than 5 minutes old.

Zapier integration

Portal.io supports Zapier natively. If you are building automations with Zapier rather than a direct integration, the same underlying events — person modifications, payment status changes, proposal status changes, and order status changes — are available as Zapier triggers. Connect the Portal.io app in Zapier to configure these without writing any code.