Webhooks

Receive real-time notifications when onboarding events happen.

Starter plan and above.

Webhooks let you receive HTTP POST requests when events happen in Portico — a client submits a response, completes an onboarding, signs a document, or makes a payment. Use them to trigger workflows in your own systems or connect to Zapier.

Setup

  1. Go to Settings > Connect Services.
  2. Enter a public HTTPS URL in the webhook field.
  3. Click Save.

Portico generates a webhook_secret on first setup. Copy it immediately — you'll need it to verify signatures. Clearing the URL clears the secret.

Events

EventFires when
onboarding.sentYou send an onboarding to a client
onboarding.completedA client completes all required fields and the onboarding is marked complete
response.submittedA client submits or resubmits a field response
response.approval_revokedAn admin revokes a previously approved response
signature.signedA client signs a signature field
payment.succeededA client's payment is successfully processed through Stripe

The response.approval_revoked event is only sent to your team webhook, not to Zapier subscriptions.

Payload format

Every webhook POST has this structure:

{
  "event": "onboarding.completed",
  "timestamp": "2026-03-15T10:30:00.000Z",
  "data": {
    "onboarding_id": "uuid"
  }
}

The data object varies by event:

EventData fields
onboarding.sentonboarding_id
onboarding.completedonboarding_id
response.submittedonboarding_id, field_id, value
response.approval_revokedonboarding_id, field_id
signature.signedonboarding_id, field_id, value
payment.succeededonboarding_id, amount_cents, payment_intent_id

Headers

Every webhook request includes these headers:

Content-Type: application/json
X-Portico-Event: onboarding.completed
X-Portico-Signature: a1b2c3d4...

X-Portico-Event is always present. X-Portico-Signature is included when a webhook secret is configured.

Verifying signatures

Verify that a webhook came from Portico by computing the HMAC-SHA256 signature of the raw request body and comparing it to the X-Portico-Signature header.

Node.js example:

import crypto from "crypto";

function verifyWebhook(body, signature, secret) {
  const expected = crypto
    .createHmac("sha256", secret)
    .update(body)
    .digest("hex");
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expected)
  );
}

Python example:

import hmac
import hashlib

def verify_webhook(body: bytes, signature: str, secret: str) -> bool:
    expected = hmac.new(
        secret.encode(), body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(signature, expected)

Use a constant-time comparison (as shown above) to prevent timing attacks.

Delivery and retries

  • Timeout — Portico waits up to 10 seconds for your endpoint to respond.
  • Retries — if the first attempt fails or times out, Portico retries once after 5 seconds.
  • No further retries — if both attempts fail, the delivery is logged and dropped. Portico does not queue or retry beyond the second attempt.
  • Non-blocking — webhook delivery never delays the action that triggered it. Your client's experience is unaffected if your endpoint is slow or down.

Your endpoint should return a 2xx status code to acknowledge receipt. The response body is ignored.

Zapier integration

Instead of handling webhooks yourself, you can connect events to Zapier. Zapier subscriptions receive the same payload format and are managed through the API or directly in Zapier.

Zapier subscriptions support 5 events: onboarding.completed, onboarding.sent, response.submitted, signature.signed, and payment.succeeded.

Common Zapier workflows

  • Onboarding completed — create a project in Asana, Notion, or Monday.
  • Client submitted — post a notification to a Slack channel.
  • Payment succeeded — add a row to a Google Sheet or log it in QuickBooks.
  • Signature signed — send a confirmation email via Gmail or Mailchimp.

Testing webhooks

During development, use a tool like webhook.site or ngrok to inspect payloads before pointing the webhook at your production endpoint.

To trigger a test event, send an onboarding to yourself and complete it — this fires onboarding.sent, response.submitted, and onboarding.completed in sequence.