Skip to main content
When you create a webhook subscription, you specify which event types you want to receive. Portal.io delivers a signed HTTP POST payload to your registered HTTPS endpoint each time a subscribed event occurs. This page documents every available event type and the shape of its payload.
Your endpoint must respond with a 2xx status code to acknowledge receipt. If Portal.io does not receive a 2xx response, it retries the delivery on this schedule: 1 minute, 5 minutes, 30 minutes, 2 hours, 12 hours.

Verifying Webhook Signatures

Every delivery includes an X-Webhook-Signature header you should use to verify the request came from Portal.io:
  • t — Unix timestamp when the webhook was generated.
  • v1 — HMAC-SHA256 hex digest.
Verification steps:
  1. Read the raw request body exactly as received.
  2. Extract t and v1 from the X-Webhook-Signature header.
  3. Rebuild the signed message: t + "." + rawBody.
  4. Compute HMAC_SHA256(secretKey, signedMessage).
  5. Compare your result to v1. Reject the request if they do not match.
  6. Reject the request if t is more than 5 minutes old.
The secretKey is the value returned in the secretKey field when you created the subscription.

Payload Envelope

All webhook events use a standard envelope format:

Event Types

Use these values in the repeated Events body parameter when creating a webhook subscription.

proposal.build.status_update

AI proposal builder finishes or fails.

proposal.outline.status_update

AI outline generation completes or fails.

proposal.status_changed

A proposal’s status changes (e.g. Draft → Submitted).

proposal.build.status_update

Fired when the Portal.io AI builder finishes generating a proposal or encounters an error during generation. Subscribe to this event if you need to react when a build completes asynchronously.

Payload Fields

The data object contains:

proposal.outline.status_update

Fired when Portal.io’s AI outline generation completes or fails. Useful for workflows that wait for an AI-generated scope before proceeding.

Payload Fields

The data object contains:

proposal.status_changed

Fired whenever a proposal’s status changes. Common transitions include Draft → Submitted, Submitted → Accepted, and Accepted → Completed.

Payload Fields

The data object contains the full proposal detail. See Proposal Object Fields for the complete structure.

Proposal Object Fields

The proposal object included in proposal.build.status_update and proposal.status_changed payloads shares the same structure.

Top-Level Fields

Proposal Statuses

Customer Fields

Location Fields

Dealer Fields

Salesperson Fields

Extends user fields with:

User Fields

Financial Summary

Sales Tax

Tax Calculation

Area Fields

Area Option Fields

Item Fields

Linked Order Fields

Change Order Fields

Profit Fields

Recurring Services

Payment Schedule

Payment Requests