FieldCamp

FieldCamp Webhook Events Catalog: Event Types and Payloads

Reference for every FieldCamp webhook event, including sample payloads, required scopes, and tips for building reliable webhook receivers.

This FieldCamp webhook events catalog documents every event type your endpoint can receive, along with sample payload shapes and the API scope required to subscribe. Use it as a companion to the Webhooks guide when you are wiring up integrations, mapping events to downstream systems, or testing receiver code against realistic payloads. The events that can be subscribed to today cover clients, jobs, visits, invoices and your custom objects. Sections further down also describe events for estimates, payments, schedules and team members: those are on the roadmap and are not subscribable yetPOST /api/v1/webhooks rejects an event name outside the seventeen listed in the Webhooks guide. Treat those sections as the shape to expect when they ship.

Before subscribing to events, read the Webhooks guide and confirm your API key is set up by following Authentication. The catalog below assumes you already know how to register a subscription and verify HMAC signatures.

How to use this catalog

The catalog below documents the body of each event type and the resource it relates to. Pair it with your receiver to confirm you handle every field your integration depends on.

The body is the payload itself — there is no wrapper

FieldCamp posts the event payload directly. There is no id, type, created_at, api_version or data wrapper around it, so a receiver that reads body.data.object will find nothing.

A job.status_changed body looks like this:

{
  "job": { "id": "6a84…", "jobNumber": "1042", "jobStatus": "completed" },
  "previousStatus": "in-progress",
  "newStatus": "completed"
}

The event name and the delivery id travel in headers instead:

HeaderValue
X-FieldCamp-Eventthe event name, e.g. job.status_changed
X-FieldCamp-Deliverythe delivery id — use this for deduplication
X-FieldCamp-Signaturelowercase hex HMAC-SHA256 of the raw body

Note the header is X-FieldCamp-Delivery with no -Id suffix. Read the event name from X-FieldCamp-Event rather than from the body, and key your deduplication on X-FieldCamp-Delivery.

Payload objects are the resource's own camelCase fields, as the API returns them — firstName, companyName, phoneNumber, createdAt — not snake_case. Where a sample below shows first_name or line1, read the corresponding resource page for the field names that actually arrive.

The keys next to the resource differ per event rather than following one rule: job.status_changed carries previousStatus and newStatus, object.stage_changed carries the stage transition, and *.deleted events carry only { "id": "…" } with no resource snapshot.

Store the full event payload alongside any state you derive from it. When you debug a downstream issue, having the original delivery — not just the parsed fields — saves hours.

The scope you need to subscribe

Subscribing takes one scope for every event: webhooks:manage. There is no per-resource scope on a subscription — a key with webhooks:manage can subscribe to any event, and a key without it cannot manage endpoints at all, whatever else it holds.

The read scopes below are what your key needs to fetch the resource afterwards, which is the usual next step once a webhook tells you something changed:

Event familyScope to read the resourceLinked resource
client.*clients:readClients
job.*jobs:readJobs
visit.*visits:readVisits
invoice.*invoices:readInvoices
object.*objects:readCustom Objects

Grant only what you need. When you create an API key from Authentication, a key that just forwards webhooks needs webhooks:manage and the read scope for whatever it fetches back.

If your API key is missing the relevant scope, the subscription creation request returns a 403 error and the event is silently skipped. Always test in staging with the same scopes you plan to use in production.

Client events

Client events fire whenever a customer record is created, updated, or deleted in your account. They are the foundation of any CRM sync.

client.created

Fires when a new client is added — manually, via import, or through the API.

{
  "type": "client.created",
  "data": {
    "object": {
      "id": "cli_01HZ...",
      "first_name": "Jordan",
      "last_name": "Reyes",
      "company_name": "Reyes HVAC",
      "email": "jordan@example.com",
      "phone": "+15035551234",
      "addresses": [{ "line1": "12 Pine St", "city": "Portland", "region": "OR" }],
      "created_at": "2026-05-29T14:22:31.812Z"
    }
  }
}

client.updated

Fires when any field on a client changes. data.previous_attributes lists the fields that were modified.

client.deleted

Fires when a client is permanently removed. The payload contains only the deleted resource's id and the timestamp.

Job events

Job events power most operations integrations — back-office sync, accounting, dispatch dashboards, and notifications.

job.created

Fires when a job is created from a request, an estimate, or directly from the Jobs API.

{
  "type": "job.created",
  "data": {
    "object": {
      "id": "job_01HZ...",
      "client_id": "cli_01HZ...",
      "title": "AC tune-up",
      "status": "scheduled",
      "scheduled_start": "2026-06-02T15:00:00Z",
      "scheduled_end": "2026-06-02T17:00:00Z",
      "assignees": ["usr_01HZ..."],
      "total": 24900
    }
  }
}

job.updated

Fires when any field on a job changes, including reschedules, assignment changes, and description edits.

job.status_changed

Fires when the job moves between statuses (for example, scheduledin_progresscompleted). Use this when you only care about lifecycle transitions and want to ignore noisy field-level edits.

job.deleted

Fires when a job is removed. The payload contains the job id, the related client_id, and the deletion timestamp.

Invoice events

Invoice events keep accounting systems and customer-facing dashboards in sync with billing changes.

invoice.created

Fires when a new invoice is generated from a completed job, from an estimate, or directly via the API.

invoice.updated

Fires when line items, totals, taxes, or memo fields change on an existing invoice. Use data.previous_attributes to see what changed.

invoice.sent

Fires when an invoice is sent to a client by email. Use this to log outbound communications or to trigger downstream reminder workflows.

invoice.paid

Fires when an invoice is marked as fully paid — either through Stripe, recorded manually, or via the API.

{
  "type": "invoice.paid",
  "data": {
    "object": {
      "id": "inv_01HZ...",
      "client_id": "cli_01HZ...",
      "total": 24900,
      "amount_paid": 24900,
      "currency": "USD",
      "status": "paid",
      "paid_at": "2026-05-29T18:11:02.443Z"
    }
  }
}

invoice.overdue

Fires when an invoice passes its due date without being paid in full. Use this to drive collections automations.

Estimate events

Estimate events let you mirror the sales pipeline in external CRMs, BI tools, or notification systems.

estimate.created

Fires when a new estimate is created from a request, manually, or via the API.

estimate.accepted

Fires when a client accepts an estimate (with or without a signature). This is often the trigger to create a downstream job in another system, or to fire a Slack notification.

estimate.declined

Fires when a client declines an estimate. Pair this with your CRM to update opportunity stages or to trigger a follow-up cadence.

Payment events

Payment events keep your books accurate and let you reconcile money movement in near real time.

payment.created

Fires when a new payment is recorded against an invoice — including Stripe charges, manual entries, and applied deposits.

{
  "type": "payment.created",
  "data": {
    "object": {
      "id": "pay_01HZ...",
      "invoice_id": "inv_01HZ...",
      "client_id": "cli_01HZ...",
      "amount": 24900,
      "currency": "USD",
      "method": "card",
      "captured_at": "2026-05-29T18:11:02.443Z"
    }
  }
}

payment.refunded

Fires when a payment is refunded in full or partially. Use data.object.refunded_amount to know how much money was returned.

Schedule and visit events

Schedule events keep dispatch boards, technician apps, and external calendars in sync as visits move on the timeline.

visit.created

Fires when a new visit is added to a job — manually on the Dispatch calendar, through AI dispatch, or via the API.

visit.updated

Fires when a visit changes — including reschedules, assignee changes, and status updates. Useful for syncing to external calendars or driving customer-facing arrival ETA notifications.

visit.completed

Fires when a technician marks a visit complete in the mobile app. This is often the trigger for invoice generation in downstream systems.

{
  "type": "visit.completed",
  "data": {
    "object": {
      "id": "vis_01HZ...",
      "job_id": "job_01HZ...",
      "assignees": ["usr_01HZ..."],
      "actual_start": "2026-05-29T15:04:11Z",
      "actual_end": "2026-05-29T16:38:55Z",
      "status": "completed"
    }
  }
}

Team member events

Team member events keep auth, scheduling, and reporting tools in sync as your crew grows or changes.

team_member.created

Fires when a new team member is added to the workspace, either invited from the dashboard or provisioned via the API.

team_member.updated

Fires when a team member's profile, role, skills, or assignment availability changes. Useful for syncing to identity providers, HRIS, or external scheduling tools.

Sample receiver flow

Verify the signature

Use the raw request body and the X-FieldCamp-Signature header as described in Webhooks. Reject any delivery that fails verification.

Look up the handler by type

Switch on event.type to route the payload to the right handler. Group rare events (for example, team_member.*) into a default handler so you log unknown types instead of dropping them.

Deduplicate by delivery ID

Store X-FieldCamp-Delivery in a short-lived cache or DB column. Skip processing if you have seen it before — FieldCamp retries can resend the same event.

Acknowledge fast, process async

Return 200 OK as soon as the event is durably queued. Do the heavier downstream work in a background worker so your endpoint stays well under the FieldCamp delivery timeout.

Reconcile with the API

On every *.updated event, optionally fetch the resource through the matching API resource — Clients, Jobs, or Visits — so you always operate on the freshest state, not just the snapshot at the moment of delivery.

Troubleshooting and FAQs

Which event should I subscribe to for invoice payment automation?
Subscribe to invoice.paid for the moment money is fully collected, and to payment.created for individual payment events (including partial payments). Most accounting integrations subscribe to both and reconcile against the invoice ID.

Do I need separate subscriptions per event family?
No — a single subscription can listen to any combination of the 20 event types. That said, splitting subscriptions by downstream consumer (one for accounting, one for the CRM, one for dispatch) is easier to monitor and re-enable when something breaks.

Why am I seeing job.updated events with no obvious change?
Some background recalculations — total, balance due, status rollups — fire job.updated. Inspect data.previous_attributes to confirm what changed. If it is empty, you can safely ignore the event.

Can I receive webhook events for custom objects or job forms?
Custom object events are not yet part of this catalog. If you need to react to job form submissions, poll the resource on a schedule or react to the related visit.completed event and then fetch the latest form data via the API.

My handler errors out on client.deleted — what is in the payload?
The delete event contains only the id of the removed client and the deleted_at timestamp. The full record is not retrievable through the API afterward, so cache the resource snapshot from client.created / client.updated if you need to log the deleted record's full details.

On this page