Webhooks
Webhooks let your integration react the moment something happens in your SMBcrm account, instead of polling the API for changes. You set them up inside a workflow, and SMBcrm sends an HTTP POST with a JSON body to your endpoint as matching events occur.
Set up a webhook
Section titled “Set up a webhook”There’s no separate API to register a webhook. You wire it up once inside a workflow:
- Create or open a workflow in your SMBcrm account.
- Add the trigger for the event you want, such as Contact Created or Appointment Booked.
- Add a Webhook action as a step in that workflow.
- Enter your HTTPS endpoint URL. This is also where you attach a shared secret (see Securing your endpoint).
- Publish the workflow.
Every contact or event that meets the trigger’s conditions now sends a request to your URL.
Events you can trigger on
Section titled “Events you can trigger on”Any trigger in the workflow builder can drive a webhook action. Common ones:
- Contact created or updated
- Inbound or outbound message (SMS, email, or other conversation activity)
- Appointment booked, rescheduled, or canceled
- Opportunity stage or status changed
- Form submitted
- Order or payment received
The trigger picker in the workflow builder shows the full list available to your account.
Payload
Section titled “Payload”Each call is an HTTP POST with a JSON body describing what happened. The exact fields depend on the trigger and on how you configured the webhook step. A contact event looks roughly like this:
{ "type": "ContactCreate", "locationId": "<location_id>", "id": "<contact_id>", "firstName": "Jordan", "lastName": "Lee", "email": "jordan@example.com", "phone": "+15125550142", "tags": ["website-lead"], "dateAdded": "2026-07-08T15:04:00.000Z"}Parse it defensively. Check that a field is present before you use it, tolerate keys you don’t recognize, and expect the shape to grow over time. When you need more than the payload carries, take the id and call the API with your Location token or PIT to fetch the full record. See Contacts.
Securing your endpoint
Section titled “Securing your endpoint”Treat every inbound request as untrusted until you verify it.
A minimal handler that does all of the above:
import express from 'express';
const app = express();app.use(express.json());
app.post('/webhooks/smbcrm', (req, res) => { // Verify the shared secret before trusting anything in the body // (check req.query.token instead if you used a URL token) if (req.header('x-webhook-secret') !== process.env.SMBCRM_WEBHOOK_SECRET) { return res.status(401).end(); }
res.status(200).end(); // acknowledge first, before doing any real work
enqueueWebhookEvent(req.body); // hand off to a queue; don't await it here});
app.listen(3000);Reliability and retries
Section titled “Reliability and retries”Delivery is at-least-once. If your endpoint doesn’t return a 2xx quickly, expect the same event again.
- Acknowledge fast. Return
2xxonce you’ve verified the secret and queued the work, not after processing finishes. - Deduplicate using an event id if the payload carries one, or a hash of the body if it doesn’t.
- Don’t rely on ordering. Two events for the same record can arrive out of order, so key your logic off data in the payload, like an updated-at timestamp, rather than the order requests land in.
Managing webhooks in code
Section titled “Managing webhooks in code”Webhooks live in workflows, so you create, change, and remove them in your account rather than through the API. There’s no REST endpoint in this reference for managing webhook subscriptions programmatically; the workflow Webhook action is how you set them up.
Related
Section titled “Related”- Workflows — the trigger-and-action model webhooks build on.
- Contacts — fetch the full record behind a lean payload.
- Conversations & Messages — the API for the message events above.
