Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.portal.io/llms.txt

Use this file to discover all available pages before exploring further.

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:
X-Webhook-Signature: t=1710000000,v1=5f2b3e7a9d1c4f8e6b2a3c9d7f4e1a6b5c3d2f7e9a1b4c6d8e0f2a3b5c7d9e1
  • 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:
{
  "id": "evt_abc123",
  "type": "proposal.build.status_update",
  "created": "2026-04-06T00:22:19Z",
  "data": { ... }
}
FieldTypeDescription
idstringUnique webhook delivery identifier, prefixed with evt_.
typestringEvent type identifier (dot-notation).
createdstring (ISO 8601)UTC timestamp when the event was generated.
dataobjectEvent-specific payload.

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:
FieldTypeDescription
data.statusstringBuild result status. One of Building, Completed, Failed.
data.proposalobjectFull proposal detail snapshot (see proposal object fields below).
{
  "id": "evt_build_abc123",
  "type": "proposal.build.status_update",
  "created": "2026-04-06T00:22:19Z",
  "data": {
    "status": "Completed",
    "proposal": {
      "id": 4123,
      "number": 1001,
      "name": "Smith Residence AV",
      "status": "Draft",
      "createdDate": "2026-04-05T18:00:00Z",
      "lastModifiedDate": "2026-04-06T00:22:19Z",
      "financialSummary": {
        "partsSubtotal": 5200.00,
        "partsTotal": 5200.00,
        "laborTotal": 800.00,
        "feeTotal": 0,
        "proposalSubtotal": 6000.00,
        "proposalTotal": 6450.00
      }
    }
  }
}

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:
FieldTypeDescription
data.proposalIdintegerID of the proposal whose outline changed.
data.statusstringOutline generation status. One of Generating, Completed, Failed.
data.outlinestring or nullThe generated outline text. Only populated when status is Completed.
{
  "id": "evt_outline_def456",
  "type": "proposal.outline.status_update",
  "created": "2026-04-06T00:20:00Z",
  "data": {
    "proposalId": 4123,
    "status": "Completed",
    "outline": "1. Living Room AV System\n2. Master Bedroom Audio\n3. Outdoor Speakers"
  }
}

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.
{
  "id": "evt_status_ghi789",
  "type": "proposal.status_changed",
  "created": "2026-04-06T00:22:19Z",
  "data": {
    "id": 4123,
    "number": 1001,
    "name": "Smith Residence AV",
    "status": "Accepted",
    "createdDate": "2026-04-05T18:00:00Z",
    "lastSubmittedDate": "2026-04-05T20:00:00Z",
    "clientLastOpenedDate": "2026-04-06T00:10:00Z",
    "clientLastDecisionDate": "2026-04-06T00:22:00Z",
    "lastCompletedDate": null,
    "lastModifiedDate": "2026-04-06T00:22:19Z",
    "lastModifiedByUserDate": "2026-04-06T00:22:19Z",
    "coverpageImageUrl": "https://files.portal.io/covers/4123.jpg",
    "aboutUs": "We specialize in custom AV installations.",
    "projectDescription": "Full home AV system including living room, bedroom, and outdoor areas.",
    "projectTerms": "Net 30 payment terms apply.",
    "lastModifiedUser": {
      "firstName": "John",
      "lastName": "Doe"
    },
    "customer": {
      "id": 88,
      "partyType": "Person",
      "contactType": "Client",
      "firstName": "Jane",
      "lastName": "Smith",
      "companyName": "",
      "contactEmail": "jane.smith@example.com",
      "contactEmailCC": "",
      "contactPhone": "(555) 123-4567",
      "location": {
        "id": 1,
        "street": "123 Main St",
        "suite": "Apt 4B",
        "city": "Austin",
        "postalCode": "78701",
        "state": "Texas",
        "stateAbbrev": "TX",
        "country": "United States",
        "phone": "(555) 123-4567"
      }
    },
    "dealer": {
      "companyName": "Premier AV Solutions",
      "location": {
        "id": 10,
        "street": "500 Commerce Dr",
        "suite": "Suite 200",
        "city": "Dallas",
        "postalCode": "75201",
        "state": "Texas",
        "stateAbbrev": "TX",
        "country": "United States",
        "phone": "(555) 987-6543"
      },
      "salesperson": {
        "firstName": "John",
        "lastName": "Doe",
        "id": 5,
        "email": "john@premierav.com"
      },
      "webSiteUrl": "https://premierav.com",
      "companyPhone": "(555) 987-6543",
      "companyLogoUrl": "https://files.portal.io/logos/premierav.png"
    },
    "financialSummary": {
      "partsSubtotal": 5200.00,
      "partsDiscountType": "Percentage",
      "partsDiscountPercentage": 0,
      "partsDiscount": 0,
      "partsDiscountTaxable": 0,
      "partsDiscountTaxExempt": 0,
      "partsTotal": 5200.00,
      "laborTotal": 800.00,
      "feeTotal": 0,
      "proposalSubtotal": 6000.00,
      "salesTax": {
        "taxStatus": "Ok",
        "total": 450.00,
        "calculation": {
          "method": "FixedPercentage",
          "applyTo": ["Parts", "Labor"],
          "partsTax": 8.25,
          "partsTaxName": "State Tax",
          "laborTax": 8.25,
          "laborTaxName": "State Tax",
          "hasMultipleTaxSupport": false,
          "partsTax2": null,
          "partsTax2Name": null,
          "laborTax2": null,
          "laborTax2Name": null,
          "feeTax": null,
          "feeTaxName": null,
          "feeTax2": null,
          "feeTax2Name": null,
          "taxLocation": null,
          "isTaxJarAvailable": false,
          "partsTotalTax": 429.00,
          "laborTotalTax": 66.00,
          "feeTotalTax": null
        }
      },
      "proposalTotal": 6450.00,
      "currency": {
        "code": "USD",
        "symbol": "$"
      }
    },
    "areas": [
      {
        "id": 1,
        "name": "Living Room",
        "options": [
          {
            "id": 1,
            "status": "Accepted",
            "lastModifiedDate": "2026-04-06T00:22:19Z",
            "clientDescription": "Full surround sound system",
            "installerDescription": "Install 5.1 system with in-wall wiring",
            "items": [
              {
                "id": 101,
                "parentId": null,
                "itemType": "Part",
                "referencedItemId": 500,
                "createdDate": "2026-04-05T18:00:00Z",
                "lastModifiedDate": "2026-04-05T18:00:00Z",
                "brand": "Sonos",
                "model": "Arc",
                "description": "Premium Smart Soundbar",
                "name": "Sonos Arc",
                "shortDescription": "Soundbar",
                "clientNote": null,
                "imageUrl": "https://files.portal.io/items/500.jpg",
                "msrp": 999.00,
                "sellPrice": 899.00,
                "cost": 650.00,
                "costUpdateDate": "2026-03-01T00:00:00Z",
                "supplier": "Acme Audio Distributors",
                "quantity": 1,
                "total": {
                  "amount": 899.00,
                  "currency": {
                    "code": "USD",
                    "symbol": "$"
                  },
                  "isCombinedPrice": false
                },
                "isTaxExempt": false,
                "isRecurringService": false,
                "linkedOrders": [
                  {
                    "orderId": 7001,
                    "orderNumber": 301,
                    "orderNumberSuffix": "A",
                    "supplier": "Acme Audio Distributors",
                    "supplierRef": "PO-12345",
                    "orderName": "Smith AV Equipment Order",
                    "orderStatus": "Submitted"
                  }
                ]
              }
            ],
            "total": 5200.00,
            "totalRecurringService": 0
          }
        ]
      }
    ],
    "profit": {
      "total": 1350.00,
      "percentage": 22.5,
      "partTotal": 1050.00,
      "partPercentage": 20.19,
      "laborTotal": 300.00,
      "laborPercentage": 37.5,
      "isProfitIncludeCos": false
    },
    "recurringServices": null,
    "paymentSchedule": {
      "customerDescription": "50% deposit, 50% on completion",
      "payments": [
        {
          "calculation": "Percentage",
          "amount": 3225.00,
          "due": {
            "date": null,
            "milestone": "Upon acceptance"
          }
        },
        {
          "calculation": "Percentage",
          "amount": 3225.00,
          "due": {
            "date": null,
            "milestone": "Upon completion"
          }
        }
      ]
    },
    "paymentRequests": [
      {
        "id": 3042,
        "status": "Paid",
        "amount": 3225.00,
        "dueDate": "2026-04-20T00:00:00Z",
        "description": "50% deposit",
        "paymentMethod": "Stripe"
      }
    ],
    "changeOrders": []
  }
}

Proposal Object Fields

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

Top-Level Fields

FieldTypeDescription
idintegerUnique identifier for the proposal.
numberintegerHuman-readable proposal number, unique within the account.
namestringDisplay name of the proposal.
statusstringCurrent proposal status (see proposal statuses).
createdDatestring (ISO 8601)When the proposal was created.
lastSubmittedDatestring or nullWhen the proposal was last submitted to the client.
clientLastOpenedDatestring or nullWhen the client last opened/viewed the proposal.
clientLastDecisionDatestring or nullWhen the client last approved or declined.
lastCompletedDatestring or nullWhen the proposal was last marked complete.
lastModifiedDatestring (ISO 8601)When the proposal was last modified by any actor.
lastModifiedByUserDatestring (ISO 8601)When the proposal was last modified by a human user (not system).
coverpageImageUrlstring or nullURL of the proposal cover page image.
aboutUsstring or null”About Us” text included in the proposal.
projectDescriptionstring or nullProject description text.
projectTermsstring or nullTerms and conditions text.
lastModifiedUserobjectUser who last modified the proposal (see user fields).
customerobject or nullCustomer contact (see customer fields).
dealerobjectDealer account (see dealer fields).
financialSummaryobject or nullFinancial breakdown (see financial summary).
areasarray or nullProposal areas with options and line items (see area fields).
profitobject or nullProfit breakdown (see profit fields).
recurringServicesobject or nullRecurring services summary (see recurring services).
paymentScheduleobject or nullPayment schedule (see payment schedule).
paymentRequestsarray or nullPayment requests (see payment requests).
changeOrdersarrayAssociated change orders (see change order fields).

Proposal Statuses

ValueDescription
UndefinedUnset/default status.
DraftProposal is being edited, not yet sent to client.
SubmittedProposal has been sent to the client.
ViewedByClientClient has opened/viewed the proposal.
AcceptedClient has accepted the proposal.
DeclinedClient has declined the proposal.
DelayedProposal has been delayed.
CompletedProposal is marked complete.
EmailFailedDelivery email to client failed.
ExpiredProposal has expired.

Customer Fields

FieldTypeDescription
idintegerContact ID.
partyTypestringPerson or Company.
contactTypestringContact classification (e.g. Client).
firstNamestring or nullFirst name.
lastNamestring or nullLast name.
companyNamestring or nullCompany name.
contactEmailstring or nullPrimary email address.
contactEmailCCstring or nullCC email address.
contactPhonestring or nullPrimary phone number.
locationobject or nullPrimary location (see location fields).

Location Fields

FieldTypeDescription
idinteger or nullLocation ID.
streetstring or nullStreet address.
suitestring or nullSuite or unit number.
citystring or nullCity.
postalCodestring or nullZIP or postal code.
statestring or nullFull state name.
stateAbbrevstring or nullTwo-letter state abbreviation.
countrystring or nullCountry name.
phonestring or nullLocation phone number.

Dealer Fields

FieldTypeDescription
companyNamestringCompany name.
locationobjectCompany location (see location fields).
salespersonobject or nullAssigned salesperson (see salesperson fields).
webSiteUrlstring or nullCompany website URL.
companyPhonestring or nullCompany phone number.
companyLogoUrlstring or nullURL of the company logo.

Salesperson Fields

Extends user fields with:
FieldTypeDescription
firstNamestringFirst name.
lastNamestring or nullLast name.
idinteger or nullUser ID.
emailstringEmail address.

User Fields

FieldTypeDescription
firstNamestringFirst name.
lastNamestring or nullLast name.

Financial Summary

FieldTypeDescription
partsSubtotalnumberSubtotal for parts before discount.
partsDiscountTypestring or nullDiscount type. One of Percentage, Fixed.
partsDiscountPercentagenumber or nullDiscount percentage applied to parts.
partsDiscountnumber or nullDiscount amount applied to parts.
partsDiscountTaxablenumber or nullTaxable portion of parts discount.
partsDiscountTaxExemptnumber or nullTax-exempt portion of parts discount.
partsTotalnumberTotal for parts after discount.
laborTotalnumberTotal labor cost.
feeTotalnumberTotal fees.
proposalSubtotalnumberSubtotal before tax.
salesTaxobject or nullSales tax details (see sales tax).
proposalTotalnumber or nullGrand total including tax.
currencyobject or nullCurrency with code (ISO 4217, e.g. USD) and symbol (e.g. $).

Sales Tax

FieldTypeDescription
taxStatusstringOne of Undefined, Ok, NoState, OutOfCountry, NoClient, NoClientAddress, IncompleteClientAddress, NoCompanyAddress, TaxableStateDeclined.
totalnumber or nullTotal sales tax amount.
calculationobject or nullTax calculation details (see tax calculation).

Tax Calculation

FieldTypeDescription
methodstringOne of ClientLocation, CompanyLocation, FixedPercentage, None.
applyToarray<string>Array of categories tax applies to. Each value is one of None, Parts, Labor, Fee.
partsTaxnumber or nullParts tax rate.
partsTaxNamestring or nullParts tax label.
laborTaxnumber or nullLabor tax rate.
laborTaxNamestring or nullLabor tax label.
hasMultipleTaxSupportbooleanWhether multiple tax rates are configured.
partsTax2number or nullSecond parts tax rate.
partsTax2Namestring or nullSecond parts tax label.
laborTax2number or nullSecond labor tax rate.
laborTax2Namestring or nullSecond labor tax label.
feeTaxnumber or nullFee tax rate.
feeTaxNamestring or nullFee tax label.
feeTax2number or nullSecond fee tax rate.
feeTax2Namestring or nullSecond fee tax label.
taxLocationobject or nullTax jurisdiction location (see location fields).
isTaxJarAvailablebooleanWhether TaxJar integration is available.
partsTotalTaxnumber or nullCalculated total tax on parts.
laborTotalTaxnumber or nullCalculated total tax on labor.
feeTotalTaxnumber or nullCalculated total tax on fees.

Area Fields

FieldTypeDescription
idintegerArea ID.
namestringArea name.
optionsarrayArea options (see area option fields).

Area Option Fields

FieldTypeDescription
idintegerOption ID.
statusstringOption status. One of Draft, Accepted, Declined.
lastModifiedDatestring (ISO 8601)When the option was last modified.
clientDescriptionstring or nullClient-facing description.
installerDescriptionstring or nullInstaller-facing notes.
itemsarray or nullLine items (see item fields).
totalnumberOption total amount.
totalRecurringServicenumberRecurring service total for this option.

Item Fields

FieldTypeDescription
idintegerItem ID.
parentIdinteger or nullParent item ID (for sub-items).
itemTypestringOne of Part, Labor, CustomItem, Fee.
referencedItemIdintegerCatalog item ID reference.
createdDatestring (ISO 8601)When the item was added.
lastModifiedDatestring (ISO 8601)When the item was last modified.
brandstring or nullItem brand.
modelstring or nullItem model.
descriptionstring or nullFull description.
namestring or nullItem name.
shortDescriptionstring or nullShort description.
clientNotestring or nullNote visible to client.
imageUrlstring or nullItem image URL.
msrpnumber or nullManufacturer’s suggested retail price.
sellPricenumber or nullSell price per unit.
costnumber or nullCost per unit.
costUpdateDatestring or nullWhen cost was last updated.
supplierstring or nullSupplier name.
quantitynumberQuantity.
totalobjectItem total with amount (number), currency (object or null), and isCombinedPrice (boolean or null).
isTaxExemptboolean or nullWhether the item is tax-exempt.
isRecurringServiceboolean or nullWhether this is a recurring service item.
linkedOrdersarray or nullLinked purchase orders (see linked order fields).

Linked Order Fields

FieldTypeDescription
orderIdintegerOrder ID.
orderNumberintegerOrder number.
orderNumberSuffixstringOrder number suffix (e.g. A, B).
supplierstring or nullSupplier name.
supplierRefstring or nullSupplier reference/PO number.
orderNamestringOrder name.
orderStatusstringOne of Undefined, Draft, Submitted, ViewedBySupplier, Accepted, Received, EmailFailed.

Change Order Fields

FieldTypeDescription
idintegerChange order ID.
numberintegerChange order number.
namestringChange order name.
statusstringStatus (see proposal statuses).
totalobject or nullTotal with changeOrderTotal (number or null) and currency (object or null).
customerobject or nullCustomer contact (see customer fields).
createdDatestring (ISO 8601)When the change order was created.
lastModifiedDatestring (ISO 8601)When last modified.
lastModifiedByUserDatestring or nullWhen last modified by a human user.

Profit Fields

FieldTypeDescription
totalnumber or nullTotal profit amount.
percentagenumber or nullOverall profit percentage.
partTotalnumber or nullProfit on parts.
partPercentagenumber or nullProfit percentage on parts.
laborTotalnumber or nullProfit on labor.
laborPercentagenumber or nullProfit percentage on labor.
isProfitIncludeCosboolean or nullWhether profit calculation includes cost of sale.

Recurring Services

FieldTypeDescription
itemsarrayRecurring service items, each with name (string), sellPrice (number), quantity (number), totalSell (number).
totalRecurringServicenumberTotal recurring service amount.

Payment Schedule

FieldTypeDescription
customerDescriptionstring or nullCustomer-facing payment schedule description.
paymentsarray or nullScheduled payments, each with calculation (string or null), amount (number), and due object containing date (string or null) and milestone (string or null).

Payment Requests

FieldTypeDescription
idintegerPayment request ID.
statusstringPayment status. One of Undefined, Draft, Submitted, Viewed, Paid, Declined, Refunded, Pending, RequiresAction, Verifying, Cancelled.
amountnumber or nullPayment amount.
dueDatestring (ISO 8601)Payment due date.
descriptionstring or nullPayment description.
paymentMethodstring or nullPayment method used.