POST request with a JSON payload to that endpoint every time one of those events occurs.
Creating a webhook
company_id is optional. Omit it (or send null) to receive matching events across every company in your organization, or set it to scope the webhook to a single company. The response includes a signing_secret:
Subscribing to events
A new webhook isn’t subscribed to anything by default. Discover the current catalog of events with List subscribable events:
See the Webhook Events reference for listing, updating, and removing subscriptions.
Receiving deliveries
Every delivery is aPOST request with a Content-Type: application/json body in the same envelope, regardless of event:
data matches the shape of the corresponding resource returned elsewhere in the API — the example above is trimmed to representative fields; a real projects.created delivery includes the full project resource. Financial breakdown fields (such as financials_total_amount and expenses_total_amount) may be omitted from a project payload depending on the permissions of the user whose action triggered the event; amount_estimated and estimated_total_amount are always present. See the Project type for the full field list.
Your endpoint has 10 seconds — connection and response combined — to return a 2xx status code. Returning anything else, or not responding in time, marks the delivery error or timeout respectively; see Retries for what happens next.
Verifying signatures
Every delivery carries aContractorsCloud-Signature header so you can confirm it came from Contractors Cloud and wasn’t tampered with in transit:
t is the Unix timestamp the delivery was sent, and v is the hex-encoded HMAC-SHA256 signature of the string <t>.<raw request body>, computed with your webhook’s signing_secret as the key. To verify a delivery:
- Extract
tandvfrom the header. - Compute
hash_hmac('sha256', "{t}.{raw_body}", signing_secret)against the raw request body bytes exactly as received — re-serializing the parsed JSON can reorder keys or change whitespace, which changes the signature. - Compare your computed value to
vusing a constant-time comparison, not==or string equality, to avoid leaking timing information.
t is too old (for example, more than 5 minutes in the past) to reduce the window for replay attacks.
Retries
If a delivery comes backerror or times out, Contractors Cloud automatically retries it up to 3 times: roughly 1 minute, then 15 minutes, then 60 minutes after the previous attempt. Each retry is a new send record with retry_parent_id pointing back at the attempt it retried, a fresh timestamp and signature, and the identical request body.
Deliveries aren’t guaranteed to arrive in order, and the same event can be delivered more than once (for example, if a retry and the original both eventually succeed). Design your endpoint to handle duplicate and out-of-order deliveries — for example, by keying off a stable field in
data rather than assuming strict arrival order.Testing and monitoring
Verify your endpoint and signature-checking logic without waiting for a real event, using Send test webhook:data set to {"id": "foo", "value": "bar"}. It works even if the webhook is disabled or has no event subscriptions yet, which makes it useful for confirming a new webhook is reachable before you subscribe it to anything.
Every delivery attempt — test or real — is recorded as a webhook send, including the exact request body sent and the response your endpoint returned, retained for 365 days. List them with List webhook sends to audit past deliveries, and watch a webhook’s error_rate as an ongoing health signal for roughly the last 90 days of activity.