Sending Data via Webhook
This page explains exactly what to send to DURUM.ai for the app to work at its full potential. Whether you use GHL, Typeform, Calendly, Stripe, Zoho, Pipedrive, or a custom tool, the rules are the same.
The 3 Absolutely Essential Things
Without these 3 elements, your event won't be processed correctly.
1. Identify Your Account (client_key)
Every webhook must be associated with your account. There are two ways to do it:
Option A -- In the URL with bid (recommended):
https://app.durum.ai/api/webhook/lead?bid=YOUR_BUSINESS_IDThis is the format the app gives you in Settings -> Integrations. The bid is a unique, unguessable identifier, safer than a readable key. All 7 standard endpoints accept it.
Option B -- In the URL with client_key:
https://app.durum.ai/api/webhook/lead?client_key=YOUR_KEYStill fully supported, no deprecation.
Option C -- In the JSON payload:
{ "client_key": "YOUR_KEY", "email": "..." }Without client_key
The event is placed in quarantine. It won't be lost, but it will need to be classified manually by the agency. Your KPIs won't be updated in real time.
2. Identify the Contact (Email or Phone)
DURUM.ai needs to identify who the prospect is. Send at minimum one of these fields:
| Field | Example | Priority |
|---|---|---|
email | john.smith@email.com | Ideal -- also used for deduplication and attribution |
phone | +15145551234 | Good backup -- E.164 format or 10 digits |
name | John Smith | Useful but insufficient alone |
Without Email or Phone
For booking, no-show and sale (generic JSON), the event is rejected (rejected: missing_contact_info response): an appointment or sale that cannot be tied to a contact is useless and would skew your KPIs. For lead and application, the event will still be recorded, but the system won't be able to:
- Detect duplicates (risk of counting the same lead twice)
- Link the lead to future actions (booking, sale)
- Enrich data with contact history
3. Send to the Right Endpoint
Each event type has its own endpoint. This is what determines the type in DURUM.ai.
| When | Endpoint | What Is Created |
|---|---|---|
| A prospect shows interest | /api/webhook/lead | Lead |
| A prospect fills out a detailed form | /api/webhook/application | Application |
| An appointment is booked | /api/webhook/booking | Booking |
| A prospect doesn't show up | /api/webhook/no-show | No-show |
| A sale is closed | /api/webhook/sale | Sale |
| A payment is received | /api/webhook/payment | Payment |
| A refund is issued | /api/webhook/refund | Refund |
| An event of your own (beyond the 7 standard) | /api/webhook/event | Custom event (named) |
Custom events (your own events)
Beyond the 7 standard events, you can send any event, under any name you want ("Demo completed", "Deal moved to stage X", "Watched 80% of webinar"...).
The event name goes in the URL; the body only carries the contact data:
POST https://app.durum.ai/api/webhook/event?client_key=YOUR_KEY&event_name=demo_completed&source=custom
Content-Type: application/json
{
"email": "client@example.com",
"phone": "+15145550199",
"value": 250,
"occurred_at": "2026-07-07T14:00:00Z"
}Recognized fields: email, phone, value, occurred_at, contact_id, calendar_name — plus the 13 attribution fields if you want to tie the event to an ad. Any other field is kept in the raw payload.
Where to create a custom event in the app
Settings -> Events -> "Add a custom event": name it, get its dedicated webhook URL + the JSON frame. It then shows up in your Conversions (Conversions page) and integration mappings (e.g. "this Calendly calendar = this event").
One event != three events
If 3 integrations all send leads, that's one Lead event fed by 3 sources — not 3 events. To split by source, use a filter at the conversion level, never a separate event. A custom event is only for a signal that does not exist among the 7 standard.
The 13 Ad Attribution Fields
These fields allow DURUM.ai to link each lead, booking, and sale to the exact ad that generated it. This powers the Marketing table, CPA calculation, ROAS, ROI, and all attribution in the app.
Without These Fields, the App Loses Its Core Value
A lead without UTMs arrives in DURUM.ai but it's impossible to know which ad it came from. Your marketing KPIs (CPA, ROAS, ROI) will be inaccurate. You won't know which ad is performing and which is wasting your budget.
The 5 UTMs (Ad Element Names)
These are the readable names of your campaigns, ads, and ad sets. DURUM.ai uses them to match events with your Meta ads.
| # | Field | Accepted Aliases | What It Does in the App | Example | Priority |
|---|---|---|---|---|---|
| 1 | utm_campaign | utmcampaign, UTM_Campaign, campaign_name | Links the event to the campaign. Used in the Marketing table, campaign filter, and per-campaign spend calculation. | bootcamp-march-2026 | Non-negotiable |
| 2 | utm_content | utmcontent, UTM_Content, ad_name, utm_ad_name | Links the event to the ad. This is THE field that connects a lead to a Meta ad. Without it, no per-ad attribution. | ad-testimonial-tx1-ix2 | Non-negotiable |
| 3 | utm_source | utmsource, UTM_Source | Identifies the traffic source. Distinguishes Meta vs Google vs organic in reports. | meta, google, tiktok, organic | Important |
| 4 | utm_medium | utmmedium, UTM_Medium | Identifies the traffic type. Distinguishes paid from organic, email, social. | paid, cpc, email, social, organic | Important |
| 5 | utm_term | utmterm, UTM_Term, utm_adset_name, adset_name | Links the event to the ad set. Enables per-audience/ad set performance analysis. | adset-lookalike-1pct, adset-retarget-30d | Recommended |
The 3 Meta IDs (Numeric Identifiers)
These are Meta Ads numeric IDs. They enable exact matching instead of name matching (more reliable if you rename a campaign).
| # | Field | Accepted Aliases | What It Does in the App | Example | Priority |
|---|---|---|---|---|---|
| 6 | utm_campaign_id | campaign_id | Exact match with the Meta campaign by ID instead of name | 120210987654321 | Recommended |
| 7 | utm_ad_id | ad_id | Exact match with the Meta ad by ID instead of name | 120210123456789 | Recommended |
| 8 | utm_adset_id | adset_id | Exact match with the Meta ad set by ID instead of name | 120210111222333 | Recommended |
Names vs IDs -- Why Both?
Names (utm_campaign, utm_content, utm_term) are readable and useful in tables. IDs (utm_campaign_id, utm_ad_id, utm_adset_id) are reliable even if you rename a campaign. Ideally, send both. If you can only send one, send the names.
The 3 Complementary Fields
| # | Field | Accepted Aliases | What It Does in the App | Example | Priority |
|---|---|---|---|---|---|
| 11 | product_key | product, funnel_key | Identifies the product or service. Powers the per-product funnel, per-product revenue, and commissions. | bootcamp_ai | Important |
| 12 | assigned_user_name | assigned_to, Deal_Owner, rep_name | Identifies the assigned sales rep. Powers the Sales Reps table and per-rep commissions. | Marie Dupont | Recommended |
| 13 | amount | value, total, Amount, Grand_Total | Dollar amount of the sale or payment. Without this field, ROI, ROAS, and revenue will be $0. | 2500 | Non-negotiable (sales/payments) |
Complete Example with ALL Attribution Fields
{
"email": "john.smith@email.com",
"phone": "+15145551234",
"name": "John Smith",
"product_key": "bootcamp_ai",
"amount": 2500,
"assigned_user_name": "Marie Dupont",
"utm_source": "meta",
"utm_medium": "paid",
"utm_campaign": "bootcamp-march-2026",
"utm_content": "ad-testimonial-tx1-ix2",
"utm_term": "adset-lookalike-1pct",
"utm_campaign_id": "120210987654321",
"utm_ad_id": "120210123456789",
"utm_adset_id": "120210111222333"
}Form Question Responses
Each client asks different questions in their forms (GHL, Typeform, Calendly). All responses are captured and stored, even if they don't match standard fields. Nothing is lost.
All responses arrive in the event's raw_payload field (JSONB format). This is a complete storage of the original payload -- every question, every answer, every custom field.
In addition to raw storage, certain fields are automatically extracted to dedicated columns (email, phone, name, UTMs). Others remain in raw_payload and are accessible for analyses, exports, or advanced filters.