Webhooks
Receive real-time event notifications from NMC, verify signatures, and handle retries safely.
Webhooks notify your server with an HTTP POST request whenever a subscribed
event happens in NMC, such as a new lead arriving or a call finishing. Each
event payload includes the resource as it looked when the event occurred.
Users with account access can manage webhook endpoints in the business’s Settings → Webhooks or create a webhook endpoint through the API (invited clients cannot). In the dashboard, choose Add webhook endpoint to create an endpoint and open its detail page, where you can reveal its signing secret, edit subscribed events, send a test event, and inspect deliveries.
Choose the events your integration needs
Section titled “Choose the events your integration needs”| Event | When it fires |
|---|---|
lead.created |
A new lead was created. Its first call may still be connected. |
lead.updated |
A lead’s stage, closure, value, note, latest activity, or current AI review changed. |
activity.created |
A call, text, form submission, or forwarded email was added to a lead, including follow-ups on an existing lead. |
activity.updated |
Activity details changed, such as call completion or recording and transcript readiness. |
contact.created |
A new contact was created. |
contact.updated |
Contact details or the contact’s latest activity changed. |
business.updated |
Public business settings changed, or tracking paused or resumed. |
Select at least one event type; each webhook endpoint receives only the events you subscribe to. Each business can have up to three webhook endpoints by default, counting stopped endpoints and those created through the API.
curl --fail-with-body --request POST \ --header "Authorization: Bearer $NMC_API_KEY" \ --header 'Content-Type: application/json' \ --data '{ "url":"https://receiver.example.com/nmc", "event_types":["lead.created","lead.updated","activity.updated"] }' \ https://api.nomissedconversions.com/v1/webhook_endpointsReplace the example URL with your public HTTPS endpoint on port 443. NMC
does not follow HTTP redirects or deliver to private network addresses. Store
both the returned webhook endpoint id and signing_secret in your server’s
secret configuration.
Verify before processing
Section titled “Verify before processing”NMC signs the raw request body using the Standard Webhooks specification.
Incoming webhook requests don’t use an NMC API key; instead, verify these three
headers against your webhook endpoint’s signing_secret:
webhook-id: Unique identifier for the event (use this to deduplicate deliveries).webhook-timestamp: Unix timestamp for this delivery attempt.webhook-signature: Versioned HMAC signature.
Install the
official Standard Webhooks library
(standardwebhooks) in your receiver. This helper verifies a standard Web
Request and returns the parsed payload:
import { Webhook } from "standardwebhooks"
export async function verifyNmcWebhook(request, signingSecret) { const rawBody = await request.text() const verifier = new Webhook(signingSecret) return verifier.verify(rawBody, { "webhook-id": request.headers.get("webhook-id") ?? "", "webhook-timestamp": request.headers.get("webhook-timestamp") ?? "", "webhook-signature": request.headers.get("webhook-signature") ?? "", })}Always read the raw request body before any JSON middleware parses or reformats it. Reject requests that fail verification without processing their contents, leave the library’s timestamp tolerance check enabled, and keep your server clock synced. Once verified, save the event before running downstream work.
Store before acknowledging
Section titled “Store before acknowledging”After verifying the signature, validate the payload and write the event (along
with any queued background job) to persistent storage that survives a server
restart. Enforce a unique database constraint on the event ID (webhook-id /
payload id) so two concurrent deliveries of the same event can’t run the same
work twice. Return a 2xx response once saved, and return 2xx right away if
you’ve already stored that event ID.
Webhook deliveries can arrive more than once or out of order. If your application keeps a local copy of NMC records, fetch the current record from the API when processing each event so a delayed event never overwrites fresher data.
Read the payload
Section titled “Read the payload”Every webhook payload includes id, type, api_version, occurred_at,
is_test, data, related, and business:
- Changed fields:
data.objectholds the resource that triggered the event.data.changed_fieldslists the public field paths that changed, such asstageortracking.state([]on*.createdevents, and a single path when an array changes or an object appears or disappears). - Activities vs. parent records: Updating an activity doesn’t necessarily
emit a separate
lead.updatedorcontact.updatedevent, even when those records are included inrelated. Subscribe toactivity.createdandactivity.updatedif you want every call, text, form, and email, including repeat calls on an open lead. - Earliest activity (
is_first_activity):is_first_activitymarks the lead’s earliest activity by occurrence time. If an activity with an earlier timestamp is attached to the lead later,is_first_activitymoves to that earlier activity: NMC sendslead.updatedwithfirst_activity_idinchanged_fields, without sendingactivity.updatedfor the activity that was previously first. - Contact matching: NMC matches each activity to a contact by phone number
first and email address second, and each email address belongs to at most one
contact. An email-only activity can therefore match a contact who already has
a phone number, and a later activity can add a phone number or email address
to an existing contact (emitting
contact.updated).
Open any event link in the table above for its complete schema and field
descriptions. For example, this business.updated test event shows a business
name change (lead.*, activity.*, and contact.* events carry those
resources in data.object instead):
{ "id": "01900000-0000-7000-8000-000000000010", "type": "business.updated", "api_version": "2026-10-01", "occurred_at": "2026-09-20T15:30:00Z", "is_test": true, "data": { "object": { "id": "01900000-0000-7000-8000-000000000020", "name": "Example Heating", "website_url": "https://www.example.com", "timezone": "America/Vancouver", "currency_code": "CAD", "tracking": { "state": "active", "since": "2026-09-01T00:00:00Z" } }, "changed_fields": ["name"] }, "related": {}, "business": { "id": "01900000-0000-7000-8000-000000000020", "name": "Example Heating", "website_url": "https://www.example.com", "timezone": "America/Vancouver", "currency_code": "CAD", "tracking": { "state": "active", "since": "2026-09-01T00:00:00Z" } }}Retries, tests, and resends
Section titled “Retries, tests, and resends”- Timeouts and retry window: Your endpoint has 15 seconds to return any
2xxresponse. NMC retries non-2xxresponses and timeouts with increasing delays, and marks the delivery Failed after roughly 3 days without a2xxresponse. - Stopping an endpoint (
410or manual toggle): Returning410stops the webhook endpoint immediately, cancels its remaining unsent deliveries, and emails the account. Turning a webhook endpoint off in the dashboard also stops it and cancels unsent deliveries. While stopped, NMC creates no deliveries for that endpoint, so events from that period are never sent. After turning it back on, you can resend any deliveries that were canceled when it stopped. - Inspecting deliveries: Open the webhook endpoint from Settings →
Webhooks and check Deliveries for Failed or in-flight attempts
(check your server logs before resending, since your server may have received
a delivery that timed out). To monitor endpoint status from code, call
GET /webhook_endpoints. - Test events: Use Send test on the webhook endpoint’s page to send a
test event for a chosen event type. Test payloads set
is_test: true; skip real customer notifications or live side effects whenis_testistrue. Send test and Resend share a limit of 10 actions per minute per business. - Retries, resends, and secret rotation: Automatic retries and manual
Resend actions keep the original event
idand JSON body, while sending to the webhook endpoint’s current URL and signing with its currentsigning_secretand a fresh timestamp. Coordinate secret changes with your receiver, and never usewebhook-timestampas your deduplication key. - Deleting an endpoint:
Removing a webhook endpoint deletes
its delivery records, though a request already in flight may still arrive.
Keep the webhook endpoint
idif your integration needs to remove it later.