Skip to content

CalSpark developer docs

A REST API and signed webhooks for your own scripts and tools. Included on the one plan. Off until an admin turns on API and Webhooks in Company, Features. API version 2026-10-01.

Machine-readable spec: /api/v1/openapi.json (OpenAPI 3.1).

Authentication

Create a key in Settings, Developers. Keys look like csk_live_... and are shown once, so copy it when you create it. Send it as a Bearer token. Each key belongs to one company and sees only that company's data.

curl https://www.calspark.app/api/v1/me \
  -H "Authorization: Bearer csk_live_..."

A key can be revoked or given an expiry date at any time. Requests with a missing, malformed, unknown, revoked or expired key return 401. If the company subscription is not active the API returns 402.

Scopes

Each key carries only the scopes you grant. Write scopes do not include read access; grant both when you need both.

ScopeAllows
customers:readList and read customers.
customers:writeCreate customers.
jobs:readList and read jobs.
jobs:writeCreate jobs.
invoices:readList and read invoices.
invoices:writeCreate draft invoices.
estimates:readList and read estimates (read-only).
payments:readList and read payments.
users:readList and read team members (name, email, role).
events:readRead the event feed.
webhooks:manageCreate, list, update and delete webhook endpoints.

A request without the needed scope returns 403 insufficient_scope. GET /api/v1/me works with any valid key.

Endpoints

Method and pathScope
GET /meany key
GET /customers, GET /customers/{id}customers:read
POST /customerscustomers:write
GET /jobs, GET /jobs/{id}jobs:read
POST /jobsjobs:write
GET /invoices, GET /invoices/{id}invoices:read
POST /invoices (creates a draft)invoices:write
GET /payments, GET /payments/{id}payments:read
GET /users, GET /users/{id}users:read
GET /eventsevents:read
GET, POST /webhook-endpoints; GET, PATCH, DELETE /webhook-endpoints/{id}webhooks:manage

Base URL https://www.calspark.app/api/v1. Money is always integer cents, timestamps are ISO 8601 in UTC, and ids are strings. An id that belongs to another company returns 404, the same as one that does not exist.

Pagination

List endpoints take limit (1 to 100, default 25) and cursor. Pass the previous response's next_cursor to get the next page. Cursors are opaque and signed; a bad one returns 400 invalid_cursor.

{ "object": "list", "data": [ ... ], "has_more": true, "next_cursor": "eyJ2Ijox..." }

Lists are newest first. GET /events is oldest first for polling, and always returns a next_cursor when it returns events, so you can resume exactly where you stopped. New events show up after a short settle delay of about two seconds.

Idempotency

Send an Idempotency-Key header (8 to 128 characters: letters, digits, _ - : .) on POST requests to make retries safe. Retrying the same key with the same request returns the original response with Idempotent-Replayed: true. The same key with a different request returns 409 idempotency_key_reuse; a retry while the first request is still running returns 409 idempotency_in_progress. Keys are remembered for 24 hours. Server errors (5xx) are not stored, so a retry runs again. Creating a webhook endpoint does not accept idempotency keys because its response contains the signing secret.

curl -X POST https://www.calspark.app/api/v1/customers \
  -H "Authorization: Bearer csk_live_..." \
  -H "Idempotency-Key: 7f9c2ba4-new-customer-1" \
  -H "Content-Type: application/json" \
  -d '{"first_name":"Dana","last_name":"Whitaker","email":"dana@example.com"}'

Errors

Every error, including malformed JSON and unknown routes, has the same shape and carries the request id that is also in the Request-Id header.

{
  "error": {
    "type": "invalid_request_error",
    "code": "validation_failed",
    "message": "Request validation failed",
    "param": "email",
    "request_id": "req_0123456789abcdef01234567",
    "details": [{ "param": "email", "message": "Invalid email address" }]
  }
}
CodeHTTP status
invalid_request400
validation_failed422
authentication_required401
invalid_api_key401
insufficient_scope403
feature_disabled403
subscription_required402
not_found404
payload_too_large413
conflict409
idempotency_key_reuse409
idempotency_in_progress409
invalid_cursor400
rate_limited429
internal_error500

Rate limits

Each key may make 120 requests per minute. Every response carries RateLimit-Limit, RateLimit-Remaining and RateLimit-Reset (Unix seconds). Over the limit you get 429 rate_limited with a Retry-After header in seconds.

Webhooks

CalSpark posts each event to the HTTPS URLs you register, signed so you can verify it came from us.

Register endpoints in Settings, Developers or with POST /api/v1/webhook-endpoints. The signing secret (whsec_...) is shown once. Every delivery is a POST with a JSON event envelope and these headers:

CalSpark-Signature: t=1760000000,v1=5257a869e7ecebeda32affa62cdca3fa51cad7e77a0e56ff536d0ce8e108d8bd

Compute HMAC SHA-256 over `${t}.${rawBody}` using your secret, hex encode it, and compare it to each v1 value in constant time. During secret rotation the header carries one v1 per active secret; accept the request if any matches. Reject timestamps more than 5 minutes from your clock. Always verify against the raw request body, before any JSON parsing.

const crypto = require('crypto');

// secrets: your endpoint signing secret(s). Pass both the new and the old secret while rotating.
function verifyCalSparkSignature(rawBody, header, secrets, toleranceSeconds = 300) {
  const parts = String(header || '').split(',');
  let timestamp = null;
  const signatures = [];
  for (const part of parts) {
    const i = part.indexOf('=');
    if (i === -1) continue;
    const key = part.slice(0, i).trim();
    const value = part.slice(i + 1).trim();
    if (key === 't' && timestamp === null) timestamp = Number(value);
    if (key === 'v1') signatures.push(value);
  }
  if (!Number.isInteger(timestamp) || signatures.length === 0) return false;
  if (Math.abs(Math.floor(Date.now() / 1000) - timestamp) > toleranceSeconds) return false;

  let ok = false;
  for (const secret of secrets) {
    const expected = crypto.createHmac('sha256', secret).update(timestamp + '.' + rawBody).digest('hex');
    for (const sig of signatures) {
      const a = Buffer.from(sig, 'utf8');
      const b = Buffer.from(expected, 'utf8');
      if (a.length === b.length && crypto.timingSafeEqual(a, b)) ok = true;
    }
  }
  return ok;
}

// Express: use the RAW body, not the parsed JSON.
// app.post('/calspark', express.raw({ type: 'application/json' }), (req, res) => {
//   const valid = verifyCalSparkSignature(req.body.toString('utf8'), req.get('CalSpark-Signature'), [process.env.CALSPARK_WEBHOOK_SECRET]);
//   if (!valid) return res.sendStatus(400);
//   const event = JSON.parse(req.body.toString('utf8'));
//   res.sendStatus(200);
// });
module.exports = { verifyCalSparkSignature };

Zapier

Planned. Not available. There is no Zapier app to install or join. This describes what a Zapier app would do on top of the API and webhooks above.

It would connect with an API key and use the same API and webhooks described above. Triggers: New Job, Job Scheduled, Job Completed, New Customer, Invoice Sent, Invoice Paid, Payment Received and New Lead. Actions: Create Customer, Create Job and Create Draft Invoice. Searches: Find Customer (by email or phone) and Find Job.

Triggers would register a webhook endpoint for you (it would count toward your limit of 20 endpoints) and fall back to polling the events feed. The key would need the scopes for what you use, plus webhooks:manage and events:read for triggers.

MCP server (Claude and other AI assistants)

Planned. Not available. A Model Context Protocol server would let an assistant read your customers, jobs, schedule and unpaid invoices, and create customers, jobs and draft invoices. There is nothing to install today.

It would authenticate with an API key, and you would start with a read-only key. It would not edit or delete anything, send invoices or take payments. The public API has no endpoint to change an existing job, so an assistant could schedule a job only when it creates it. Until the server exists, the API and webhooks above are the supported way to connect tools.

Slack notifications

Post to a Slack channel from an automation, without installing a Slack app.

In Slack, create an incoming webhook for a channel. In CalSpark, open Settings, Integrations, and paste the URL. Only https://hooks.slack.com addresses are accepted. The URL is stored encrypted and is never shown again. Use Send test message to check it.

Prebuilt notifications are available for new leads, completed jobs and paid invoices, and any custom automation can include a Slack step. Custom automations are under Settings, Automations. Messages are sent from CalSpark's servers; Slack bills nothing for incoming webhooks, but your Slack plan applies.

CSV export

Download customers, jobs and invoices as CSV from Settings, Data. Company admins only.

Each export can be limited to a date range (days in your company time zone) and is capped at 100,000 rows. Timestamps are ISO 8601 UTC, money is in dollars with two decimals, and the column list is shown beside each download. Notes and internal fields are not included. Exports are rate limited and recorded in the audit log, and they do not trigger webhooks or events.

Event catalog

Every event type with a real example payload. The envelope is identical everywhere; data.object is the resource, and previous_attributes holds the values that changed.

customer.created

{
  "id": "evt_00000000000000000000000000000001",
  "type": "customer.created",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "1042",
      "object": "customer",
      "first_name": "Dana",
      "last_name": "Whitaker",
      "company_name": null,
      "email": "dana@example.com",
      "phone": "+15555550123",
      "customer_type": "residential",
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}

customer.updated

{
  "id": "evt_00000000000000000000000000000002",
  "type": "customer.updated",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "1042",
      "object": "customer",
      "first_name": "Dana",
      "last_name": "Whitaker",
      "company_name": null,
      "email": "dana@example.com",
      "phone": "+15555550123",
      "customer_type": "residential",
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "phone": null
    }
  }
}

customer.deleted

{
  "id": "evt_00000000000000000000000000000003",
  "type": "customer.deleted",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "1042",
      "object": "customer",
      "first_name": "Dana",
      "last_name": "Whitaker",
      "company_name": null,
      "email": "dana@example.com",
      "phone": "+15555550123",
      "customer_type": "residential",
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}

job.created

{
  "id": "evt_00000000000000000000000000000004",
  "type": "job.created",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "5081",
      "object": "job",
      "customer_id": "1042",
      "address_id": "adr_example",
      "scope_of_work": "Replace water heater",
      "status": "pending",
      "scheduled_start": null,
      "scheduled_end": null,
      "actual_start": null,
      "actual_end": null,
      "en_route_at": null,
      "assigned_user_ids": [
        "usr_example"
      ],
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}

job.scheduled

{
  "id": "evt_00000000000000000000000000000005",
  "type": "job.scheduled",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "5081",
      "object": "job",
      "customer_id": "1042",
      "address_id": "adr_example",
      "scope_of_work": "Replace water heater",
      "status": "scheduled",
      "scheduled_start": "2026-10-08T14:00:00.000Z",
      "scheduled_end": "2026-10-08T16:00:00.000Z",
      "actual_start": null,
      "actual_end": null,
      "en_route_at": null,
      "assigned_user_ids": [
        "usr_example"
      ],
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}

job.rescheduled

{
  "id": "evt_00000000000000000000000000000006",
  "type": "job.rescheduled",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "5081",
      "object": "job",
      "customer_id": "1042",
      "address_id": "adr_example",
      "scope_of_work": "Replace water heater",
      "status": "scheduled",
      "scheduled_start": "2026-10-08T14:00:00.000Z",
      "scheduled_end": "2026-10-08T16:00:00.000Z",
      "actual_start": null,
      "actual_end": null,
      "en_route_at": null,
      "assigned_user_ids": [
        "usr_example"
      ],
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "scheduled_start": "2026-10-07T14:00:00.000Z",
      "scheduled_end": "2026-10-07T16:00:00.000Z"
    }
  }
}

job.unscheduled

{
  "id": "evt_00000000000000000000000000000007",
  "type": "job.unscheduled",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "5081",
      "object": "job",
      "customer_id": "1042",
      "address_id": "adr_example",
      "scope_of_work": "Replace water heater",
      "status": "pending",
      "scheduled_start": null,
      "scheduled_end": null,
      "actual_start": null,
      "actual_end": null,
      "en_route_at": null,
      "assigned_user_ids": [
        "usr_example"
      ],
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "scheduled"
    }
  }
}

job.assigned

{
  "id": "evt_00000000000000000000000000000008",
  "type": "job.assigned",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "5081",
      "object": "job",
      "customer_id": "1042",
      "address_id": "adr_example",
      "scope_of_work": "Replace water heater",
      "status": "scheduled",
      "scheduled_start": "2026-10-08T14:00:00.000Z",
      "scheduled_end": "2026-10-08T16:00:00.000Z",
      "actual_start": null,
      "actual_end": null,
      "en_route_at": null,
      "assigned_user_ids": [
        "usr_example"
      ],
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "assigned_user_ids": []
    }
  }
}

job.unassigned

{
  "id": "evt_00000000000000000000000000000009",
  "type": "job.unassigned",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "5081",
      "object": "job",
      "customer_id": "1042",
      "address_id": "adr_example",
      "scope_of_work": "Replace water heater",
      "status": "scheduled",
      "scheduled_start": "2026-10-08T14:00:00.000Z",
      "scheduled_end": "2026-10-08T16:00:00.000Z",
      "actual_start": null,
      "actual_end": null,
      "en_route_at": null,
      "assigned_user_ids": [
        "usr_example"
      ],
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "assigned_user_ids": [
        "usr_example"
      ]
    }
  }
}

job.en_route

{
  "id": "evt_0000000000000000000000000000000a",
  "type": "job.en_route",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "5081",
      "object": "job",
      "customer_id": "1042",
      "address_id": "adr_example",
      "scope_of_work": "Replace water heater",
      "status": "scheduled",
      "scheduled_start": "2026-10-08T14:00:00.000Z",
      "scheduled_end": "2026-10-08T16:00:00.000Z",
      "actual_start": null,
      "actual_end": null,
      "en_route_at": null,
      "assigned_user_ids": [
        "usr_example"
      ],
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}

job.status_changed

{
  "id": "evt_0000000000000000000000000000000b",
  "type": "job.status_changed",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "5081",
      "object": "job",
      "customer_id": "1042",
      "address_id": "adr_example",
      "scope_of_work": "Replace water heater",
      "status": "in_progress",
      "scheduled_start": "2026-10-08T14:00:00.000Z",
      "scheduled_end": "2026-10-08T16:00:00.000Z",
      "actual_start": "2026-10-08T14:05:00.000Z",
      "actual_end": null,
      "en_route_at": null,
      "assigned_user_ids": [
        "usr_example"
      ],
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "scheduled"
    }
  }
}

job.completed

{
  "id": "evt_0000000000000000000000000000000c",
  "type": "job.completed",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "5081",
      "object": "job",
      "customer_id": "1042",
      "address_id": "adr_example",
      "scope_of_work": "Replace water heater",
      "status": "completed",
      "scheduled_start": "2026-10-08T14:00:00.000Z",
      "scheduled_end": "2026-10-08T16:00:00.000Z",
      "actual_start": "2026-10-08T14:05:00.000Z",
      "actual_end": "2026-10-08T15:50:00.000Z",
      "en_route_at": null,
      "assigned_user_ids": [
        "usr_example"
      ],
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "in_progress"
    }
  }
}

job.cancelled

{
  "id": "evt_0000000000000000000000000000000d",
  "type": "job.cancelled",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "5081",
      "object": "job",
      "customer_id": "1042",
      "address_id": "adr_example",
      "scope_of_work": "Replace water heater",
      "status": "cancelled",
      "scheduled_start": "2026-10-08T14:00:00.000Z",
      "scheduled_end": "2026-10-08T16:00:00.000Z",
      "actual_start": null,
      "actual_end": null,
      "en_route_at": null,
      "assigned_user_ids": [
        "usr_example"
      ],
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "scheduled"
    }
  }
}

job.deleted

{
  "id": "evt_0000000000000000000000000000000e",
  "type": "job.deleted",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "5081",
      "object": "job",
      "customer_id": "1042",
      "address_id": "adr_example",
      "scope_of_work": "Replace water heater",
      "status": "pending",
      "scheduled_start": null,
      "scheduled_end": null,
      "actual_start": null,
      "actual_end": null,
      "en_route_at": null,
      "assigned_user_ids": [
        "usr_example"
      ],
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}

job.photo_added

{
  "id": "evt_0000000000000000000000000000000f",
  "type": "job.photo_added",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "pho_example",
      "object": "job_photo",
      "job_id": "5081",
      "photo_type": "after",
      "caption": "Finished install",
      "created": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}

invoice.created

{
  "id": "evt_00000000000000000000000000000010",
  "type": "invoice.created",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "inv_example",
      "object": "invoice",
      "number": "INV-2026-0042",
      "customer_id": "1042",
      "job_id": "5081",
      "status": "draft",
      "currency": "usd",
      "subtotal": 45000,
      "tax": 3600,
      "discount": 0,
      "total": 48600,
      "amount_paid": 0,
      "amount_due": 48600,
      "invoice_date": "2026-10-06T00:00:00.000Z",
      "due_date": "2026-11-05",
      "paid_at": null,
      "sent_at": null,
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}

invoice.sent

{
  "id": "evt_00000000000000000000000000000011",
  "type": "invoice.sent",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "inv_example",
      "object": "invoice",
      "number": "INV-2026-0042",
      "customer_id": "1042",
      "job_id": "5081",
      "status": "sent",
      "currency": "usd",
      "subtotal": 45000,
      "tax": 3600,
      "discount": 0,
      "total": 48600,
      "amount_paid": 0,
      "amount_due": 48600,
      "invoice_date": "2026-10-06T00:00:00.000Z",
      "due_date": "2026-11-05",
      "paid_at": null,
      "sent_at": "2026-10-06T15:30:00.000Z",
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "draft"
    }
  }
}

invoice.viewed

{
  "id": "evt_00000000000000000000000000000012",
  "type": "invoice.viewed",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "inv_example",
      "object": "invoice",
      "number": "INV-2026-0042",
      "customer_id": "1042",
      "job_id": "5081",
      "status": "viewed",
      "currency": "usd",
      "subtotal": 45000,
      "tax": 3600,
      "discount": 0,
      "total": 48600,
      "amount_paid": 0,
      "amount_due": 48600,
      "invoice_date": "2026-10-06T00:00:00.000Z",
      "due_date": "2026-11-05",
      "paid_at": null,
      "sent_at": "2026-10-06T15:30:00.000Z",
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "sent"
    }
  }
}

invoice.paid

{
  "id": "evt_00000000000000000000000000000013",
  "type": "invoice.paid",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "inv_example",
      "object": "invoice",
      "number": "INV-2026-0042",
      "customer_id": "1042",
      "job_id": "5081",
      "status": "paid",
      "currency": "usd",
      "subtotal": 45000,
      "tax": 3600,
      "discount": 0,
      "total": 48600,
      "amount_paid": 48600,
      "amount_due": 0,
      "invoice_date": "2026-10-06T00:00:00.000Z",
      "due_date": "2026-11-05",
      "paid_at": "2026-10-06T15:30:00.000Z",
      "sent_at": "2026-10-06T15:30:00.000Z",
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "sent"
    }
  }
}

invoice.partially_paid

{
  "id": "evt_00000000000000000000000000000014",
  "type": "invoice.partially_paid",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "inv_example",
      "object": "invoice",
      "number": "INV-2026-0042",
      "customer_id": "1042",
      "job_id": "5081",
      "status": "partially_paid",
      "currency": "usd",
      "subtotal": 45000,
      "tax": 3600,
      "discount": 0,
      "total": 48600,
      "amount_paid": 20000,
      "amount_due": 28600,
      "invoice_date": "2026-10-06T00:00:00.000Z",
      "due_date": "2026-11-05",
      "paid_at": null,
      "sent_at": "2026-10-06T15:30:00.000Z",
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "sent"
    }
  }
}

invoice.overdue

{
  "id": "evt_00000000000000000000000000000015",
  "type": "invoice.overdue",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "inv_example",
      "object": "invoice",
      "number": "INV-2026-0042",
      "customer_id": "1042",
      "job_id": "5081",
      "status": "overdue",
      "currency": "usd",
      "subtotal": 45000,
      "tax": 3600,
      "discount": 0,
      "total": 48600,
      "amount_paid": 0,
      "amount_due": 48600,
      "invoice_date": "2026-10-06T00:00:00.000Z",
      "due_date": "2026-11-05",
      "paid_at": null,
      "sent_at": "2026-10-06T15:30:00.000Z",
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "sent"
    }
  }
}

invoice.voided

{
  "id": "evt_00000000000000000000000000000016",
  "type": "invoice.voided",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "inv_example",
      "object": "invoice",
      "number": "INV-2026-0042",
      "customer_id": "1042",
      "job_id": "5081",
      "status": "void",
      "currency": "usd",
      "subtotal": 45000,
      "tax": 3600,
      "discount": 0,
      "total": 48600,
      "amount_paid": 0,
      "amount_due": 48600,
      "invoice_date": "2026-10-06T00:00:00.000Z",
      "due_date": "2026-11-05",
      "paid_at": null,
      "sent_at": "2026-10-06T15:30:00.000Z",
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "sent"
    }
  }
}

invoice.updated

{
  "id": "evt_00000000000000000000000000000017",
  "type": "invoice.updated",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "inv_example",
      "object": "invoice",
      "number": "INV-2026-0042",
      "customer_id": "1042",
      "job_id": "5081",
      "status": "sent",
      "currency": "usd",
      "subtotal": 45000,
      "tax": 3600,
      "discount": 0,
      "total": 48600,
      "amount_paid": 0,
      "amount_due": 48600,
      "invoice_date": "2026-10-06T00:00:00.000Z",
      "due_date": "2026-11-05",
      "paid_at": null,
      "sent_at": "2026-10-06T15:30:00.000Z",
      "created": "2026-10-06T15:30:00.000Z",
      "updated": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "due_date": "2026-10-30"
    }
  }
}

payment.received

{
  "id": "evt_00000000000000000000000000000018",
  "type": "payment.received",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "pay_example",
      "object": "payment",
      "invoice_id": "inv_example",
      "amount": 48600,
      "currency": "usd",
      "method": "card",
      "status": "completed",
      "paid_at": "2026-10-06T15:30:00.000Z",
      "created": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}

payment.failed

{
  "id": "evt_00000000000000000000000000000019",
  "type": "payment.failed",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "pay_example",
      "object": "payment",
      "invoice_id": "inv_example",
      "amount": 48600,
      "currency": "usd",
      "method": "card",
      "status": "failed",
      "paid_at": "2026-10-06T15:30:00.000Z",
      "created": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}

payment.refunded

{
  "id": "evt_0000000000000000000000000000001a",
  "type": "payment.refunded",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "pay_example",
      "object": "payment",
      "invoice_id": "inv_example",
      "amount": 48600,
      "currency": "usd",
      "method": "card",
      "status": "refunded",
      "paid_at": "2026-10-06T15:30:00.000Z",
      "created": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}

lead.created

{
  "id": "evt_0000000000000000000000000000001b",
  "type": "lead.created",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "led_example",
      "object": "lead",
      "name": "Sam Rivera",
      "email": "sam@example.com",
      "phone": null,
      "source": "website",
      "message": "Need a quote for a panel upgrade.",
      "created": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}

estimate.sent

{
  "id": "evt_0000000000000000000000000000001c",
  "type": "estimate.sent",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "est_example",
      "object": "estimate",
      "estimate_number": "EST-2026-0007",
      "status": "sent",
      "customer_id": "1042",
      "total": 125000,
      "currency": "usd",
      "expires_on": "2026-11-05",
      "decided_by_name": null,
      "decline_reason": null,
      "converted_job_id": null,
      "converted_invoice_id": null,
      "created_at": "2026-10-06T15:30:00.000Z",
      "resend": false
    },
    "previous_attributes": {}
  }
}

estimate.viewed

{
  "id": "evt_0000000000000000000000000000001d",
  "type": "estimate.viewed",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "est_example",
      "object": "estimate",
      "estimate_number": "EST-2026-0007",
      "status": "viewed",
      "customer_id": "1042",
      "total": 125000,
      "currency": "usd",
      "expires_on": "2026-11-05",
      "decided_by_name": null,
      "decline_reason": null,
      "converted_job_id": null,
      "converted_invoice_id": null,
      "created_at": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "sent"
    }
  }
}

estimate.approved

{
  "id": "evt_0000000000000000000000000000001e",
  "type": "estimate.approved",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "est_example",
      "object": "estimate",
      "estimate_number": "EST-2026-0007",
      "status": "approved",
      "customer_id": "1042",
      "total": 125000,
      "currency": "usd",
      "expires_on": "2026-11-05",
      "decided_by_name": "Dana Whitaker",
      "decline_reason": null,
      "converted_job_id": null,
      "converted_invoice_id": null,
      "created_at": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "viewed"
    }
  }
}

estimate.declined

{
  "id": "evt_0000000000000000000000000000001f",
  "type": "estimate.declined",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "est_example",
      "object": "estimate",
      "estimate_number": "EST-2026-0007",
      "status": "declined",
      "customer_id": "1042",
      "total": 125000,
      "currency": "usd",
      "expires_on": "2026-11-05",
      "decided_by_name": "Dana Whitaker",
      "decline_reason": "Going with another quote",
      "converted_job_id": null,
      "converted_invoice_id": null,
      "created_at": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {
      "status": "viewed"
    }
  }
}

estimate.converted

{
  "id": "evt_00000000000000000000000000000020",
  "type": "estimate.converted",
  "api_version": "2026-10-01",
  "created": 1791300600,
  "company_id": "cmp_example",
  "data": {
    "object": {
      "id": "est_example",
      "object": "estimate",
      "estimate_number": "EST-2026-0007",
      "status": "approved",
      "customer_id": "1042",
      "total": 125000,
      "currency": "usd",
      "expires_on": "2026-11-05",
      "decided_by_name": "Dana Whitaker",
      "decline_reason": null,
      "converted_job_id": "5082",
      "converted_invoice_id": null,
      "created_at": "2026-10-06T15:30:00.000Z"
    },
    "previous_attributes": {}
  }
}