Skip to content
Draft — not yet reviewedThis article is a starter draft. The steps may not match the product exactly yet.

Webhooks

Webhooks move call data between Moja and your systems in real time. They come in two directions, and the direction determines almost everything else about how you configure them.

Direction Portal location What it does
Outgoing Webhooks → Outgoing Moja sends an HTTP request to your endpoint when a call event fires
Incoming Webhooks → Incoming You send an HTTP request to Moja to pass caller data, update revenue, update tags, or update fields

Each outgoing webhook triggers on one event type. These are the common ones. The complete and current list is in Webhooks → Outgoing in the dashboard when you create or edit a webhook — treat the dashboard as authoritative if it disagrees with this table.

Event Fires when
call.incoming A caller dials a tracking number and the platform begins processing
call.connected The call connects to a target
call.hangup Either party ends the call
call.failure The call cannot be connected to a target — busy, no answer, or error
call.completed Post-call processing finishes, including conversion evaluation and logging
call.converted The call meets the conversion criteria. Fires after hangup
call.on_5_secs_mark Connected duration reaches 5 seconds
call.on_15_secs_mark Connected duration reaches 15 seconds
call.on_30_secs_mark Connected duration reaches 30 seconds
call.on_45_secs_mark Connected duration reaches 45 seconds
call.on_60_secs_mark Connected duration reaches 60 seconds

There is no fixed Moja payload. You define the entire request — URL, HTTP method, headers, and body — as a template, and Moja substitutes [TAG_NAME] placeholders with real call data at dispatch time.

Supported methods: GET, POST, PUT, PATCH, DELETE. Supported body formats: JSON (application/json) and form data (application/x-www-form-urlencoded).

{
"call_id": "[CALL_ID]",
"caller": "[CALLER_ID]",
"campaign": "[CAMPAIGN_NAME]",
"duration": "[DURATION]",
"converted": "[CONVERTED]",
"revenue": "[REVENUE_PAID_OUT]",
"tags": {
"source": "[UTM_SOURCE]",
"state": "[CALLER_STATE]"
}
}

Placeholders work in the URL and query string too:

https://your-api.com/webhook?call_id=[CALL_ID]&campaign=[CAMPAIGN_ID]
Field Tier Notes
Endpoint URL Required Use HTTPS
Event type Required One event per webhook. Create several webhooks to cover several events
HTTP method Required GET, POST, PUT, PATCH, or DELETE
Body template Required for methods with a body Uses [TAG_NAME] placeholders
Body format Required JSON or form data
Custom headers Recommended Where you put your own Authorization header or a shared secret
Campaign or target attachment Required to receive traffic Attach under Campaign → Outgoing webhooks, or on a target or RTB formula

Tags are the placeholder vocabulary. The full, current list appears in the dashboard when you build the template; the groups below are the ones most integrations use.

Call identification — [CALL_ID], [CALL_SID], [INBOUND_CALL_ID], [ORG_ID], [FROM], [TO], [CALLER_ID], [CALLER_ID_NO_PLUS], [CALLER_ID_NUMERIC], [CALLER_ZIP], [CALLER_STATE], [STATE], [ZIP_CODE], [ORIGINAL_STATE], [ORIGINAL_ZIP_CODE]

Campaign and publisher — [CAMPAIGN_ID], [CAMPAIGN_NAME], [PUBLISHER_ID], [PUBLISHER_NAME], [PUBLISHER_NUMBER], [PAYOUT], [PAYOUT_NUMERIC]

Target and buyer — [TARGET_ID], [TARGET_NAME], [TARGET_TYPE], [BUYER_ID], [BUYER_NAME], [TARGET_PAYOUT], [PUBLISHER_PAYOUT]

Conversion and revenue — [CONVERTED], [REVENUE_PAID_OUT], [PUBLISHER_PAID_OUT], [DURATION], [INBOUND_DURATION], [OUTBOUND_DURATION], [CALL_TERMINATION_BY], [CALL_CONVERTED_AT], [EVENT_TIMESTAMP], [CONFIGURED_PUBLISHER_PAYOUT], [CONFIGURED_BUYER_REVENUE], [OVERWRITTEN_BUYER_REVENUE], and the _NUMERIC variants of the configured amounts

Attribution — populated for calls that came through DNI: [GCLID], [FBCLID], [UTM_SOURCE], [UTM_MEDIUM], [UTM_CAMPAIGN], [UTM_CONTENT], [UTM_TERM], [KEYWORD], [VISITOR_ID], [LANDING_PAGE], [LANDING_PAGE_DOMAIN], [LANDING_PAGE_PATH], [REFERRER], [VISITOR_CITY], [VISITOR_STATE], [VISITOR_ZIP], [VISITOR_COUNTRY], [VISITOR_IP], [VISITOR_BROWSER]

Custom tags — any custom tag you define is available as its key in uppercase. A tag with key lead_type becomes [LEAD_TYPE].

Four types, each with its own endpoint and request shape. Create them under Webhooks → Incoming; saving generates the URL and auth key.

Type Endpoint Purpose
call_data /call-data Pass caller metadata before or during routing
update_revenue /update-revenue Update revenue and conversion data on a call
update_tags /update-tags Add or remove custom tags on a call
update_fields /update-fields Update several call record fields in one request

All four authenticate with moja_auth_key as a query parameter:

POST https://webhooks.moja.cloud/{webhook_type}?moja_auth_key={your_auth_key}

Requests without a valid key return 401 Unauthorized.

Sends caller metadata ahead of the call. Moja matches it to an incoming call by caller ID, and applies any fields that correspond to custom tags you have defined.

Terminal window
curl -X POST 'https://webhooks.moja.cloud/call-data?moja_auth_key=YOUR_AUTH_KEY' \
-H 'Content-Type: application/json' \
-d '{
"caller_id": "+13105559876",
"lead_source": "google_ads",
"ad_campaign": "medicare_q1",
"landing_page": "medicare-info"
}'
Field Tier Type Notes
caller_id Required string Matched against incoming calls
transaction_id Recommended string Idempotency key
Any other field Optional string Stored with the request; applied as a tag if it matches a defined custom tag

The match lookback window is configurable per webhook. The default is 15 days.

Documented in full on the postbacks page, which is the same endpoint viewed as a conversion-reporting workflow.

Inbound. Send a stable transaction_id with each logical event. When the same organization and transaction_id arrive again after a completed attempt, Moja returns the original stored response rather than applying the request a second time. A request still in flight with that ID may return 409 Conflict — wait and retry with the same ID. Server-side idempotency is best effort, so make the sending system safe: one stable ID per event, and never retry the same event under a new ID.

Outbound. Your endpoint must respond within 10 seconds. Return 200 OK immediately and do the real work asynchronously. Deduplicate on [CALL_ID], since the same call can legitimately trigger several events. Failed deliveries are logged under Webhooks → Outgoing Webhook Requests.

Outgoing:

  1. Use the Test Webhook button in the dashboard to send a sample payload to your endpoint.
  2. Confirm your endpoint returns 200 OK inside 10 seconds.
  3. Attach the webhook to a campaign, target, or RTB formula.
  4. Place a test call and confirm the delivery appears under Webhooks → Outgoing Webhook Requests with a success status.
  5. Confirm every placeholder in your template resolved to a value, and that no raw [TAG_NAME] strings arrived.

Incoming:

  1. Send a request with a known-good moja_auth_key and confirm you get a 200 with "success": true.
  2. Send one with a bad key and confirm you get 401 — this proves you are hitting the endpoint you think you are.
  3. Confirm the effect landed on the call record in reporting, not just that the request was accepted.

Authenticating deliveries to your endpoint

Section titled “Authenticating deliveries to your endpoint”

Moja does not sign outgoing webhook payloads with a shared secret. Authenticate them by putting your own credential in the custom headers of the webhook template:

{
"Authorization": "Bearer your-api-token",
"X-Custom-Header": "custom-value"
}
Symptom Likely cause Fix
No webhooks arriving The webhook was saved but never attached to a campaign, target, or RTB formula Attach it under Campaign → Outgoing webhooks
Still no webhooks after attaching Endpoint unreachable, or your firewall is blocking Moja Check delivery status under Webhooks → Outgoing Webhook Requests, then test your endpoint from outside your network
Payload contains literal [CONVERTED] That tag is not populated for the event that fired Move the tag to a later event such as call.completed, or handle placeholder strings in your receiver
Placeholders never substitute Tag name is lowercase, misspelled, or missing brackets Use uppercase inside square brackets
Deliveries time out Your endpoint does work before responding Return 200 OK first, process asynchronously
401 Unauthorized on an incoming webhook moja_auth_key missing from the URL, or invalid Copy the exact URL from Webhooks → Incoming; header auth is not supported
Duplicate processing on your side Several events fired for one call, or a retry Deduplicate on [CALL_ID], and send a stable transaction_id on inbound requests