ClientCasa
API

Webhook Events

All webhook event types, payloads, and signature verification for ClientCasa integrations.

Webhooks send real-time HTTP POST notifications to your server when events occur in ClientCasa. Use them to trigger workflows, sync data, and build custom integrations.

Configuration

Configure webhooks in your dashboard at Settings > Webhooks. For each endpoint you can:

  • Set a destination URL that receives POST requests
  • Choose which events to subscribe to
  • Add a signing secret for HMAC-SHA256 signature verification
  • Enable or disable the endpoint without deleting it

Signature Verification

When you configure a signing secret, ClientCasa includes two headers on every delivery for verification:

HeaderDescription
X-SignatureHMAC-SHA256 signature in the format sha256=hex_digest
X-Webhook-TimestampUnix timestamp (seconds) for replay protection

The signature is computed over {timestamp}.{body}. Verify it like this:

const crypto = require('crypto')

function verifyWebhookSignature(body, timestamp, signature, secret) {
  // Reject old timestamps (> 5 minutes) to prevent replay attacks
  const now = Math.floor(Date.now() / 1000)
  if (Math.abs(now - parseInt(timestamp)) > 300) {
    throw new Error('Timestamp too old')
  }

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${timestamp}.${body}`)
    .digest('hex')

  const received = signature.replace('sha256=', '')

  if (
    !crypto.timingSafeEqual(
      Buffer.from(expected, 'hex'),
      Buffer.from(received, 'hex'),
    )
  ) {
    throw new Error('Invalid signature')
  }
}

Always verify signatures

If you configure a signing secret, always verify the signature before processing the payload. Use timingSafeEqual to prevent timing attacks.

Event Types

Proposal & Document Events

A proposal is one client-facing page assembled from blocks. These events track it through its lifecycle. (The smart_file_ prefix is ClientCasa's internal name for these documents — subscribe to the literal event strings below.)

EventDescription
smart_file_sentA proposal has been sent to a client
smart_file_viewedA client has opened a proposal for the first time
smart_file_acceptedA client has accepted (and signed, if applicable) a proposal
smart_file_completedA proposal's required actions are all complete
smart_file_expiredA proposal has passed its expiration date
client_revision_requestedA client has asked for changes to a proposal

Contract Events

These track standalone contracts (sent for e-signature) through their lifecycle.

EventDescription
contract_sentA contract has been sent to its signers
contract_viewedA signer has opened the contract for the first time
contract_signedThe contract has been signed
contract_recipient_signedAn individual signer has completed their signature
contract_countersignedThe contract has been countersigned by your side
contract_completedAll required signatures are complete
contract_expiredA contract has passed its expiration date without being signed
contract_voidedA contract has been voided
contract_amendedAn amendment to a contract has been sent for signing, or has been fully signed

Invoice & Payment Events

EventDescription
invoice_sentAn invoice has been sent to a client
invoice_viewedA client has opened an invoice for the first time
invoice_due_soonAn invoice is approaching its due date
invoice_overdueAn invoice has passed its due date
invoice_payment_reminderA payment reminder has been sent for an outstanding invoice
invoice_paidA client has paid an invoice
invoice_refundedA payment on an invoice has been refunded
invoice_payment_failedA payment attempt has failed
invoice_disputedA payment has been disputed
invoice_closed_payment_receivedA payment arrived on an invoice that was already closed — voided, written off, or the deposit of an offer that had ended. The payment is recorded and nothing is refunded automatically; you decide whether to refund it or re-open the offer. data.closedPayment carries paymentId, amount and closedAs (void, written_off or offer_ended)
payment_processingA client started a bank (ACH) payment that is still clearing
payment_receivedA payment was recorded (any method — card, ACH, cash, check, transfer)
payment_appliedA payment was applied to an invoice
payment_unappliedA payment was removed from the invoice it was applied to
payment_refundedA payment of any type was refunded
payment_chargebackA payment was charged back (Stripe dispute funds withdrawn)
payment_deletedA recorded payment was deleted
subscription_payment_failedAn automatic recurring-billing charge failed
recurring_billing_restartedAn open invoice's automatic charge changed — billing restarted on a card, resumed, moved to a new billing contact's card, or a promised charge was held. Read data.cause and data.billingStatus (below)

recurring_billing_restarted carries why it fired and the billing status it left the client in (as written, never guessed), so an automation never reports a restart that did not happen:

data.causeWhat happeneddata.billingStatus
new_cardA suspended client's billing contact saved a new card; the open invoice is tried at the next billing runactive
new_card_after_declineThe billing contact saved a card — a new one, or through the setup link — for an invoice declined on an earlier card; it is charged once at the next billing runactive
new_card_while_pausedA new card arrived while you had billing paused — nothing restarted yet; the invoice is tried when billing resumespaused
resume_after_new_cardThat paused client resumed (your Resume, or the date you set); the re-armed invoice is chargedactive or past_due
owner_resumeYou resumed a suspended client on its card on file; the retry is scheduled a few days out and the client is told firstactive
handoffYou made a contact with a card on file the billing contact; the open invoices are charged on that card a few days out, after the contact is emailed the dateactive or paused
card_supersededA charge you were told about was not made: its card is no longer the billing contact's. It is moved to the card on file (the contact is emailed the date) or waits for a new cardthe client's status
resumedBilling resumed with declined invoices still open; they are named, and not chargedactive or past_due

data also includes client (id, name), contact (the billing contact), retriedInvoices (id, invoiceNumber — what is charged), and the title and message the owner sees.

Sample payload

recurring_billing_restarted names why it fired (cause), the status it left the client in, the client, the billing contact, and the open invoices it charges (invoiceNumber is null for an invoice that has no number yet). title and message are the same words you see in your notifications. retriedInvoices can be empty.

{
  "event": "recurring_billing_restarted",
  "timestamp": "2026-09-24T14:30:00.000Z",
  "data": {
    "cause": "new_card",
    "billingStatus": "active",
    "client": {
      "id": "client_abc123",
      "name": "Rivera Studio"
    },
    "contact": {
      "id": "contact_def456",
      "name": "Jordan Rivera"
    },
    "retriedInvoices": [
      { "id": "inv_ghi789", "invoiceNumber": "INV-0042" }
    ],
    "title": "Automatic billing restarted for Rivera Studio",
    "message": "Jordan Rivera saved a new card, so automatic billing is back on. We’ll try INV-0042 once more on the new card. If it doesn’t go through, billing is suspended — and if the card is declined outright, it waits for a new card."
  }
}

Payout Events (bank deposits)

EventDescription
payout_paidA Stripe payout has successfully deposited to your bank account. Payload includes deposit amount, bank last 4, and reconciliation totals (grossTotal, feeTotal, refundTotal, paymentCount) — all four are OMITTED on a deposit that has not been reconciled yet, rather than sent as 0. lastReconciledAt says which case you have: present, the totals beside it were measured, and it is when; absent, nobody has matched this deposit to your payments yet and those totals are unknown rather than zero. It is often absent on this event even for a deposit that reconciles moments later — the notification is queued the instant the deposit is recorded, before it is walked.
payout_failedA Stripe payout failed to deposit. Payload includes the failure code and human-readable failure message.

Receipt Events

EventDescription
receipt_sentA receipt has been sent to a client
receipt_viewedA client has opened a receipt for the first time

Sale Events

A sale is money you took without an invoice first — at a market, a walk-up, or through a connected store. Sale events carry the same fields as invoice events, plus the sale's channel, external ID, tax, and refund details. The buyer is optional, so client and contact are absent on an anonymous sale.

EventDescription
sale_completedA sale has been recorded
sale_receipt_sentA sale receipt has been sent to the buyer
sale_viewedThe buyer has opened their sale receipt for the first time
sale_refundedA payment on a sale has been refunded
sale_voidedA sale has been voided

Payment Schedule Events

EventDescription
schedule_deposit_readyA deposit invoice is ready
schedule_installment_dueAn installment payment is due
schedule_installment_paidAn installment payment has been received
schedule_completedAll payments in a schedule are complete

Document Intelligence Events

EventDescription
document_processedA document has finished AI processing
document_needs_reviewA document has actions requiring manual review
document_failedDocument processing has failed
document_action_createdAn AI-proposed action is awaiting approval
document_action_approvedAn AI-proposed action has been approved

Entity Events

EventDescription
client_createdA new client has been created
contact_createdA new contact has been created
project_createdA new project has been created
time_entry_createdA new time entry has been logged

Project & Other Events

EventDescription
form_submission_completedA form (e.g. a questionnaire) was filled out and submitted
milestone_approachingA project milestone is approaching its due date
timer_running_reminderA time-tracking timer has been left running

Messaging Events

EventDescription
client_replyA client has replied to one of your emails or messages
email_delivery_failedAn email to a client could not be delivered (it bounced or was rejected)

Run of Show Events

These track event days (the minute-by-minute schedules behind Run of Show) and their timeline items through the run-up and the day itself.

EventDescription
event_day_createdA new event day (schedule) has been created
event_day_publishedAn event day has been published to its read-only client portal
event_day_archivedAn event day has been archived
timeline_item_completedAn item was marked done in the day-of Run Day view
timeline_reminder_sentA day-of SMS reminder was sent for a timeline item (when SMS reminders are configured)

Sample payloads

event_day_created, event_day_published, and event_day_archived share the same event-day shape:

{
  "event": "event_day_published",
  "timestamp": "2026-03-14T15:30:00.000Z",
  "data": {
    "id": "evd_abc123",
    "name": "Smith Wedding",
    "date": "2026-09-19",
    "status": "published",
    "projectId": "proj_xyz789"
  }
}

timeline_item_completed and timeline_reminder_sent are shaped around the timeline item:

{
  "event": "timeline_item_completed",
  "timestamp": "2026-09-19T21:42:00.000Z",
  "data": {
    "id": "tli_def456",
    "name": "Reception entrance",
    "eventDayId": "evd_abc123",
    "status": "completed"
  }
}

Lead & lifecycle events

A lead is a client at an early lifecycle stage, so the whole lead-to-customer journey is covered by these events (they replaced the older inquiry events).

EventDescription
lead_createdA new lead has been captured (e.g. an inquiry form submission)
lead_status_changedA lead moved between lifecycle stages (new → contacted → qualified)
client_wonA lead became an active client
client_lostA lead was marked declined or lost
follow_up_dueA follow-up you set on a client or lead is due

Payload Format

All webhook deliveries use this envelope format:

{
  "event": "invoice_paid",
  "timestamp": "2026-03-14T15:30:00.000Z",
  "data": {
    "documentType": "invoice",
    "documentId": "inv_abc123",
    "documentNumber": "INV-0042",
    "amount": 2500.00,
    "client": {
      "id": "client_xyz",
      "name": "Acme Corp"
    },
    "contact": {
      "id": "contact_123",
      "name": "Jane Smith",
      "email": "jane@acme.com"
    }
  }
}

The data object varies by event type but always includes identifiers for the relevant resources. Payment-related events include additional fields:

{
  "event": "invoice_paid",
  "timestamp": "2026-03-14T15:30:00.000Z",
  "data": {
    "documentType": "invoice",
    "documentId": "inv_abc123",
    "documentNumber": "INV-0042",
    "amount": 2500.00,
    "client": {
      "id": "client_xyz",
      "name": "Acme Corp"
    },
    "contact": {
      "id": "contact_123",
      "name": "Jane Smith",
      "email": "jane@acme.com"
    },
    "paymentMethod": "card",
    "last4": "4242",
    "brand": "visa"
  }
}

A payment on a closed invoice (invoice_closed_payment_received) adds the payment that arrived and how the invoice was closed, beside the invoice's own fields:

{
  "event": "invoice_closed_payment_received",
  "timestamp": "2026-09-24T18:05:00.000Z",
  "data": {
    "documentType": "invoice",
    "documentId": "inv_abc123",
    "documentNumber": "INV-0042",
    "amount": 600.00,
    "status": "void",
    "client": { "id": "client_xyz", "name": "Acme Corp" },
    "closedPayment": {
      "paymentId": "pay_def456",
      "amount": 600.00,
      "closedAs": "void"
    }
  }
}

Retry Policy

Delivery retries

Your endpoint must respond with a 2xx status code within 10 seconds or the delivery is considered failed. Each event is attempted up to 5 times before being marked failed. Ensure your handler returns quickly --- offload heavy processing to a background job.

Auto-disable after repeated failures

If an endpoint returns 5 consecutive failures (non-2xx responses, timeouts, or connection errors), ClientCasa automatically disables it to stop wasting deliveries. A single successful delivery resets the failure counter. Once you have fixed your endpoint, re-enable it from Settings > Webhooks.

Inbound webhooks

The events above are outbound — ClientCasa delivering notifications to your endpoints. ClientCasa also receives webhooks from the services that power its money, messaging, and signing features:

  • Stripe — payment and payout reconciliation (payment intent state changes, charges, disputes, payouts).
  • Resend — inbound email replies (routed into your message threads) and delivery feedback (bounces, complaints).
  • Firma — e-signature status updates for contracts and agreements.

These inbound integrations are platform-managed and not user-configurable — there is nothing to set up, and they are listed here only so you understand how data flows into your account.

On this page