What is a webhook for?
Without webhooks, for another system to find out about a new lead, someone has to copy it by hand or that tool has to keep checking MB Suite. A webhook works the other way around: as soon as the event happens, MB Suite sends the data to the URL you gave it. That way, every lead that comes in through your forms or your ads reaches your ERP or your spreadsheet, every deal that moves forward triggers the flow you built in Make, and every post that goes out can be logged wherever your team tracks it, except what a platform's terms don't allow sending to a webhook (see below).
The webhooks in this article go out of MB Suite. If you're after the opposite —another tool sending leads into your CRM—, that's set up with a Generic webhook lead source: see Connect forms and ads, and know which campaign each lead came from.
Who can create webhooks?
Only the owner and the admins of the workspace —or of the Account, if the webhook belongs to an Account— see the Webhooks tab in API & MCP; the rest of the team only sees MCP and Documentation. To create them, the API and the MCP must be turned on in the workspace. If someone turned them off, webhooks stop sending new events and Create webhook stays disabled until they're turned back on; what was already queued waits up to 72 hours (see API, MCP and webhooks). A workspace's webhooks can also listen to its Accounts; an Account's webhooks only hear what happens in that Account. Each organization can have as many active webhooks at a time as its plan allows: the counter above the table shows how many you have out of the maximum, and each plan's quotas are on Pricing.
A webhook depends on whoever created it: if that person leaves the organization or stops being an owner or admin, the webhook is disabled and the owners and admins get a notification. In the table it shows as Disabled, with the reason when you hover over it, and any owner or admin can reactivate it.
How do I create a webhook?
- Go to Settings > API & MCP and open the Webhooks tab.
- Press Create webhook.
- In Destination URL, paste the address your tool gave you. It must start with
https://and be public: MB Suite checks it as you type and, if it works, shows Valid destination with the server's name; if not, the field tells you why. Private and internal addresses aren't accepted. - Optionally, write a Description to remember what it's for (for example, “Leads to Odoo”).
- Under Events, check the ones you want to receive. At the top there's a search box; below it, the events grouped by area, with the CRM first, and each area and each module has its own box to check them all. Each event comes with its description and View example, which shows how it arrives, with sample data. All events checks every module's events at once: check only the ones you need.
- If you're in a workspace and also want your Accounts' events, turn on Also listen to Accounts and choose All Accounts (including the ones you create later) or Choose Accounts. Each event carries in
org_idthe organization where it happened, so you know which Account it comes from. - If your destination needs it, open Advanced (more on that below).
- Press Create webhook. The signing secret appears in the same window: copy it with Copy, store it, and close with Done, which becomes available once you copy it or check I saved it.
whsec_— is shown only once and can't be viewed again. Store it where your system keeps its secrets, not in a shared document. If you lose it, use Rotate secret to get a new one.What's under Advanced?
Format: Full (recommended) sends the whole record —the deal, the person, the company— and is the most convenient for Make or Odoo; IDs only sends just the identifiers, and your system fetches the rest. If a full event goes over 256 KB, it arrives with IDs only, with truncated: true inside data.
API version: leave it as it comes. Every delivery reports it in the MB-Api-Version header.
Static headers: up to 5 name-and-value pairs that travel with every delivery, for destinations that require their own token. Values are never shown again: when you edit the webhook you only see the names, and to change them you use Replace headers. They can't replace the headers MB Suite sets (the ones listed further down).
How do I test a webhook?
In the row's Actions menu (the three dots), choose Send test event. A webhook.test event reaches your destination, signed and with the same headers as a real one, and you'll see it under View deliveries. It's how you check that your system receives it and that your signature check works. The test event arrives even if the webhook is disabled: that way you can try your destination before reactivating it. While the API and the MCP are turned off, nothing goes out, not even the test event or a resend.
The test event carries a short message, not a lead or a deal. To see the real shape of an event without touching your data, use the practice organization: once your workspace has it (it's created with the first test API key, see API keys), the Live / Test selector shows up above the table. Under Test you create the practice organization's webhooks: they only receive the events of what test API keys do, and they're delivered for real, with the same signature.
Which events can I receive?
There are events from almost every MB Suite module. Each one says in its description when it fires —some only when the change comes through the API, the MCP or MIA—, so read it before building your flow. These are the CRM's, the most used, and what fires each one:
| Event | When it arrives |
|---|---|
crm.lead.created | A new lead comes in: through a lead source (your MB Suite form, a Meta lead ad, your booking page, the generic webhook, or an Instagram or Facebook direct message or comment that enters the CRM), with the first WhatsApp message from a new contact, through the API, or because someone added a prospect by hand. It carries the person, the prospect or deal the lead became, and the source. It also arrives for someone who was already in the CRM, as long as the lead creates a new prospect or deal for them. It doesn't arrive when nothing new is created: the same lead sent again, a source set to Skip duplicate, or a lead that goes to the prospect or deal the person already had. It doesn't arrive yet for a social media message or comment from a profile that's already linked to a person. It doesn't arrive for leads from Google Ads, TikTok or LinkedIn (their forms, and TikTok's direct messages and comments): those platforms' terms don't allow sending their data to webhooks. |
crm.deal.created | A deal is created: by hand in the CRM, through the API, when a prospect is converted into a deal (from the screen, the API or the AI Setter), or when a lead source sends the lead straight to a pipeline. In that last case crm.lead.created arrives too, with the deal inside. |
crm.deal.stage_changed | A deal moves to another stage or pipeline: from the screen, in a bulk move or from a sequence. It carries the new stage and pipeline and, in previous, the ones it came from, with their names. The moves made by a merge or by deleting a stage don't fire it. |
crm.deal.won | A deal is marked as won. previous_status says which status it had before. |
crm.deal.lost | A deal is marked as lost, with the lost reason if one was picked. |
crm.deal.reopened | A won or lost deal is open again. |
crm.deal.archived | A deal is archived (archived: true) or unarchived (archived: false) from its record or through the API. Archiving it along with a person or a company doesn't fire it. |
crm.deal.updated | Other data on a deal is edited: title, value, owner, company, labels, custom fields… changed_fields says which. A new stage, a win, a loss, a reopening or archiving sends its own event instead, and bulk changes to labels or owner don't fire it. |
crm.deal.deleted | A deal is deleted, one at a time or several at once. It carries the deal's last copy, which stays 30 days in the CRM's Trash. Deleting it along with its linked records, or merging it, doesn't fire it. |
crm.person.created | Someone creates a person in the CRM, or one is created through the API. A person who comes in as a new lead through a source is sent as crm.lead.created, and imports don't fire it. |
crm.person.updated | Someone edits a person's data from their record or through the API. changed_fields says what changed. Bulk changes don't fire it. |
crm.person.deleted | A person is deleted, one at a time or several at once; they stay 30 days in the Trash. Deleting them along with their linked records, or merging them, doesn't fire it. |
crm.company.created | Someone creates a company in the CRM, or one is created through the API. The companies that a lead source or an import creates don't fire it. |
crm.company.updated | Someone edits a company's data from its record or through the API. changed_fields says what changed. Bulk changes don't fire it. |
crm.company.deleted | A company is deleted, one at a time or several at once; it stays 30 days in the Trash. Deleting it along with its linked records, or merging it, doesn't fire it. |
crm.activity.created | Someone logs an activity —a note, task, call, meeting or reply— on a deal, person, company or prospect, in the CRM or through the API. What gets logged on its own (emails synced from a mailbox, WhatsApp or social messages, and what sequences, the dialer or the AI Team log) doesn't fire it. |
webhook.test | Only when you press Send test event, and only to that webhook. |
Nor does any event arrive for a deal or a person that came in through a Google Ads form, TikTok or LinkedIn —not even when the deal is created, changes stage or is won—, or for a person or a company that was created or completed through TikTok or LinkedIn: as with crm.lead.created, those platforms' terms don't allow sending their data to a webhook. Companies created or completed with a Google Ads form, and people completed with one, arrive without the form's answers in their custom fields. And in every CRM event, an attribution to Google Ads, LinkedIn or TikTok comes with its ids, without the names of the campaign, ad group, ad and form (or the Google keyword), and people and companies come without their LinkedIn link.
What's deleted can be recovered for those 30 days from the CRM Trash. And a webhook receives what happens from the moment you create it —it may take up to a minute to start— and doesn't bring in what happened before.
crm.lead.created. Keep it in mind before you run it: your destination gets one event per lead that creates something new; a duplicate doesn't send one. The backfill of a Google Ads or LinkedIn source doesn't send it, as with the rest of their leads.Some examples from other modules:
| Event | When it arrives |
|---|---|
web_forms.submission.received | A published web form gets a real response. It carries what was sent, the CRM lead it created if the form is linked to a lead source, and the ad attribution. Responses that the spam filters drop and test submissions from the editor don't fire it. |
social_planner.post.published | A post went out on all its networks, right away or at its scheduled time. It carries the link when the network provides one. It doesn't arrive for a post that goes to LinkedIn, TikTok or Google Business, even if it also goes to other networks: their terms don't allow sending their data to a webhook. |
social_planner.post.failed | A post couldn't be published on any network after the automatic retries, with the reason on each network. Like the previous one, it doesn't arrive for a post that goes to LinkedIn, TikTok or Google Business. |
social_inbox.item.received | A comment, a direct message or a mention from Instagram or Facebook arrives in the Inbox (those from LinkedIn and TikTok don't fire it: their terms don't allow sending their data to webhooks). The direct messages that belong to the CRM and what's classified as spam don't fire it. |
email_marketing.campaign.sent | An email campaign goes out —from the screen, through the API or at its scheduled time—, with how many messages were queued. |
task_management.comment.added | Someone comments on a task, from the app, the API, the MCP or MIA. Comments on private tasks don't fire it. |
reports.pdf.ready | A report's PDF finished generating, from the Reports screen or through the API. The event doesn't carry the file: it's downloaded through the API, unless the report has blocks from a platform whose terms don't allow it (see API, MCP and webhooks). |
alerts.alert.triggered | The hourly check finds that a metric alert's condition is met, once per trigger. It doesn't arrive for an alert on a Google Ads, Business Profile, LinkedIn, TikTok or YouTube metric: their terms don't allow sending their data to a webhook. |
In other modules, an event with data from Google Ads, Business Profile, LinkedIn, TikTok or YouTube doesn't arrive either, or arrives without that data: for example, a change in one of their ad accounts, a report delivery whose PDFs include their accounts, a LinkedIn or TikTok connection that gets disconnected, or a media plan's spend. The events that only carry that data, like those of Local SEO or Auto-Budget, or the media plan cost sync, aren't offered. If a webhook was already subscribed to one of them, the event stays in its list but doesn't arrive, and a retry or a resend of an event with that data doesn't go out either: the delivery shows as Cancelled.
The full list, with an example of each event, is in the picker (View example) and in the webhook events reference of the documentation.
What does my system receive, and how should it answer?
Every delivery is a POST with Content-Type: application/json. The body always has the same shape: id (the event's, evt_…), type, created_at, api_version, org_id and data, with the event's data in snake_case. The headers bring MB-Signature (the signature), MB-Event-Id, MB-Event-Type, MB-Webhook-Id, MB-Delivery-Id, MB-Api-Version, Idempotency-Key and a User-Agent that starts with MB-Suite-Webhooks.
Your system has to answer with a 2xx code within 10 seconds. If it has a lot to do, have it answer first and process afterwards. Any other answer, a redirect (3xx) or going past 10 seconds counts as a failure and is retried.
id —it also travels in Idempotency-Key— so you don't process it twice.How do I check that the event comes from MB Suite?
Every delivery travels signed in the MB-Signature header, shaped like this: t=1762000000,v1=5257a8…. t is when it was signed, in Unix seconds, and v1 is a hex HMAC-SHA256 computed with your secret over t, a dot and the exact body of the request.
- Read the request body exactly as it arrived, without turning it into JSON first: if you parse it and serialize it again, the signature no longer matches.
- Split
tand thev1values out of the header. - Compute the HMAC-SHA256 of
t.bodywith your secret and compare it with eachv1using a constant-time comparison. If any of them matches, the event comes from MB Suite. - Reject the event if
tis more than 5 minutes away from your current time: that way nobody can replay an old delivery to you.
You don't need to write it from scratch: under the secret, Verify the signature on your server has the code ready for Node, Python and PHP. While a secret is being rotated, the header carries two v1= values, and either one is valid.
How do I connect it to Make, Odoo or Monday?
Make. Create a scenario that starts with the “Custom webhook” module: Make gives you a URL, which you paste into Destination URL. Then click “Re-determine data structure” in Make and use Send test event so it learns the envelope; for the real shape of a lead or a deal, trigger the event. Make answers 200 as soon as it receives, so the delivery shows as Delivered even if the scenario fails later. As a lock, the simplest option is a static header (for example, X-Make-Token with a long value you make up) and a filter in the scenario that compares it.
Odoo. In recent versions, an automation rule with the “On webhook” trigger gives you a URL to paste into Destination URL. To create the lead, the action has to be Python code (“Execute Code”), which receives the JSON in payload: the name, for example, is in payload['data']['person']['full_name'], and payload['id'] helps you avoid creating the same lead twice. Since the action gets the JSON already parsed, checking the signature there isn't practical: treat Odoo's URL as a secret and don't share it.
Monday. Monday doesn't take webhooks from other systems on its own. The most direct route is to put Make in the middle: a scenario that starts with the MB Suite webhook and goes on with the Monday module that creates the item on your board, using the fields under data (for example, data.person.full_name, data.deal.title or data.stage.name). There are also apps in Monday's marketplace that receive webhooks.
What happens if my system doesn't answer?
MB Suite retries every delivery that fails, with longer and longer waits between one attempt and the next: 5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and, from there on, every 12 hours, up to 11 attempts over about 72 hours. Once an attempt works, that delivery is marked Delivered and isn't sent again.
If your destination goes 24 hours without answering well, the owner and the admins get an email, and the table shows under the URL “Not responding since…”, with how long it has been failing, and that it turns off automatically after 72 hours. If it's still failing at 72 hours, the webhook switches to Disabled after failures: it stops receiving events, its queued deliveries are cancelled, and another email and a notification arrive.
How do I reactivate it and recover what didn't arrive?
- Fix what was failing at your destination. View deliveries shows the code and the answer of every attempt.
- In the webhook's Actions menu, choose Reactivate.
- Leave Resend what failed before it was disabled checked —it comes checked— and confirm with Reactivate.
- The webhook receives new events again and, if you checked the resend, the deliveries that failed or were cancelled in the 72 hours before it was disabled go back into the queue, once each, oldest first. The confirmation message tells you how many were resent.
How do I see what was sent and resend a delivery?
- In the webhook's Actions menu, choose View deliveries. The Deliveries panel opens with the last 100, newest first.
- Each row shows the status (Pending, Retrying, Delivered, Failed or Cancelled), the event, the attempt (for example, Attempt 3/11), how long it took and the code your destination answered. Filter with All, Failed, Delivered or Pending.
- Open a delivery to see its detail: every attempt with its result, the Request sent (headers and body) and your destination's Last response, up to its first 4 KB. The signature and the values of your static headers aren't shown.
- To send it again, press Resend: a new delivery of the same event goes out, marked Resend, with its own series of retries. The webhook has to be active.
Events and deliveries are kept for 30 days.
How do I change the secret without breaking the integration?
- In the webhook's Actions menu, choose Rotate secret.
- Confirm with Rotate. The New secret appears: copy it and store it, just like the first one.
- Swap it in your system. For 24 hours, every delivery carries two
v1=signatures —one with the new secret and one with the old— and either is valid; after that, only the new one signs.
What else can I do with a webhook?
From the row's Actions menu: Edit changes the URL (it's checked again), the description, the events, the Accounts or what's under Advanced, without touching the secret; Disable pauses it —it stops receiving events and its queued deliveries are cancelled— until you reactivate it; Delete removes the webhook and its secret, cancels what was queued, and can't be undone. All of this also works while the API and the MCP are turned off.
A webhook takes your contacts' data —names, emails, phone numbers— out of MB Suite and into the system you choose. Subscribe only to the events you need and send them to destinations you control. The use of webhooks and the handling of that data are governed by our Terms and Conditions and our Data Processing Agreement (DPA). Next step: Integrations: what you can connect and how they work.
Still have questions?
Ask MIA, the MB Suite AI assistant: open it with the MB AI button (⌘J) in the top bar. MIA knows the section you're in, so you can ask it about what you see on screen — and it can also answer how-to questions about using MB Suite.