Skip to main content

Webhooks — API Reference

ContentsPal can send HTTP POST requests to an external URL when job-related events occur. This allows integrating with external systems (ERPs, CRMs, etc.) in near real-time.

Written by Pal
Updated over 2 weeks ago

Configuration

Organization admins configure webhooks in Organization → Integrations tab:

  • Endpoint URL — HTTPS URL that will receive POST requests.

  • Enabled — Toggle to enable/disable delivery without removing the URL.

  • Signing Secret (optional) — Shared secret for HMAC-SHA256 payload signature verification.

The webhook config is stored on the organization's settings.webhook field.

Events

Event

Trigger

job.created

A new job is created in the organization

job.updated

A job's name, customer, or insurance details are modified

job.deleted

A job is deleted

Only jobs linked to the organization (via sharing.organizationId) trigger webhooks.

HTTP Request

Method: POST

Headers:

Header

Description

Content-Type

application/json

User-Agent

ContentsPal-Webhook/1.0

X-Webhook-Event

Event type (e.g. job.created)

X-Webhook-Signature

sha256={hex} — HMAC-SHA256 of the request body using the signing secret. Only present when a signing secret is configured.

Timeout: 30 seconds. Requests that take longer are aborted.

Payload Schema

{
  "event": "job.created",
  "timestamp": "2026-03-03T14:30:00.000Z",
  "organizationId": "abc123",
  "job": {
    "id": "job-uuid",
    "name": "Smith Residence - Fire Damage",
    "status": "inventory",
    "customer": {
      "name": "John Smith",
      "email": "[email protected]",
      "phone": "555-1234"
    },
    "insurance": {
      "claimNumber": "CLM-2026-001",
      "company": "State Farm",
      "lossType": "fire",
      "policyNumber": "POL-123",
      "agent": "Jane Doe",
      "phone": "555-5678",
      "email": "[email protected]"
    },
    "ownerUserId": "user-uuid",
    "createdAt": "2026-03-01T10:00:00.000Z",
    "updatedAt": "2026-03-03T14:30:00.000Z"
  },
  "changes": [
    {
      "field": "settings.customer",
      "oldValue": { "name": "J Smith" },
      "newValue": { "name": "John Smith", "email": "[email protected]", "phone": "555-1234" }
    }
  ]
}

Field Details

Field

Type

Description

event

string

One of job.created, job.updated, job.deleted

timestamp

string

ISO-8601 timestamp of when the event was dispatched

organizationId

string

Organization UUID

job.id

string

Job UUID

job.name

string

Job name

job.status

string?

Job status (e.g. inventory, packout, completed)

job.customer

object?

Customer details (name, email, phone)

job.insurance

object?

Insurance details (claimNumber, company, lossType, etc.)

job.ownerUserId

string

UUID of the user who owns the job

job.createdAt

string

Job creation timestamp

job.updatedAt

string

Job last-update timestamp

changes

array?

Only present for job.updated. Lists which watched fields changed with old/new values.

Watched Fields (for job.updated)

  • jobName — The job's display name

  • settings.customer — Customer contact details (name, email, phone)

  • settings.insurance — Insurance claim details (claimNumber, company, lossType, policyNumber, agent, etc.)

Verifying Signatures

If a signing secret is configured, verify the X-Webhook-Signature header:

const crypto = require('crypto');function verifySignature(body, secret, signatureHeader) {
  if (typeof signatureHeader !== 'string') {
    return false;
  }  const expected = 'sha256=' + crypto
    .createHmac('sha256', secret)
    .update(body, 'utf8')
    .digest('hex');  const expectedBuffer = Buffer.from(expected, 'utf8');
  const receivedBuffer = Buffer.from(signatureHeader, 'utf8');  if (expectedBuffer.length !== receivedBuffer.length) {
    return false;
  }  return crypto.timingSafeEqual(expectedBuffer, receivedBuffer);
}

Error Handling

  • Webhooks are fire-and-forget. Failed deliveries (non-2xx responses, timeouts, network errors) are logged server-side but not retried.

  • Your endpoint should return a 2xx status code to acknowledge receipt.

  • Delivery is best-effort: events may occasionally be missed during outages or function cold starts.

Did this answer your question?