Send revenue postbacks
Who this is for: Engineers connecting a CRM, order system, buyer platform, or billing workflow to Moja.
What you’ll need: An incoming webhook of type update_revenue, its auth key, and a completed test call.
Time required: About 20 minutes.
What this endpoint does
Section titled “What this endpoint does”The revenue postback endpoint updates buyer revenue on a call record that already exists. Use it when the final sale, conversion, or billable amount is confirmed somewhere outside Moja and needs to be written back against the call that produced it.
It updates one field: revenue_paid_out, which represents buyer or call revenue on the call record. revenue_paid_out is the canonical public field name.
It is also not a pre-call call_data webhook, an outgoing webhook, an RTB bid endpoint, or a bulk reporting adjustment API. See Webhooks for those.
Before you begin
Section titled “Before you begin”- Confirm you have access to Webhooks → Incoming in the Moja portal.
- Know which identifier your external system can supply for a call.
call_log_idis strongly preferred over phone number. - Have one completed test call available with a real connected duration.
Set it up
Section titled “Set it up”-
Create the incoming webhook.
Go to Webhooks → Incoming → Create and set the type to
update_revenue. -
Set the caller ID match window.
Set Caller ID Match Days between 1 and 7 days if you will ever match calls by phone number. The UI default is 7 days. If you will only ever match by
call_log_id, this setting does not apply. -
Copy the generated endpoint URL.
The URL includes your
moja_auth_key. Copy it from the portal after creation rather than assembling it by hand — the copied URL is the source of truth if configuration changes.Expected result: you hold a URL of the form
https://webhooks.moja.cloud/update-revenue?moja_auth_key=YOUR_AUTH_KEY. -
Optionally link the webhook to a campaign.
Under the campaign’s Incoming Webhooks section. Linking is optional for
update_revenue: revenue updates are scoped by the organization auth key, and campaign attachment does not restrict which calls can be updated.
Endpoint
Section titled “Endpoint”POST or GET https://webhooks.moja.cloud/update-revenue?moja_auth_key=YOUR_AUTH_KEYPOST requests require Content-Type: application/json.
Authentication is moja_auth_key as a query parameter. Header-based authentication is not supported for this endpoint. Missing or invalid authentication returns 401 Unauthorized; rate-limited requests may return 429 Too Many Requests.
Worked example
Section titled “Worked example”curl -X POST 'https://webhooks.moja.cloud/update-revenue?moja_auth_key=YOUR_AUTH_KEY' \ -H 'Content-Type: application/json' \ -d '{ "call_log_id": "abc123-def456-ghi789", "revenue_paid_out": 150.00, "transaction_id": "order-10001" }'Response:
{ "success": true, "call_log_ids": ["abc123-def456-ghi789"], "revenue_paid_out": 150}Send up to 50 call_log_id values when the same amount applies to several known calls.
curl -X POST 'https://webhooks.moja.cloud/update-revenue?moja_auth_key=YOUR_AUTH_KEY' \ -H 'Content-Type: application/json' \ -d '{ "call_log_id": [ "abc123-def456-ghi789", "def456-ghi789-jkl012" ], "revenue_paid_out": 150.00, "transaction_id": "order-10001" }'Do not combine a call_log_id array with phone-number matching.
Use only when no call_log_id is available.
curl -X POST 'https://webhooks.moja.cloud/update-revenue?moja_auth_key=YOUR_AUTH_KEY' \ -H 'Content-Type: application/json' \ -d '{ "phone_number": "+13105559876", "revenue_paid_out": 150.00, "transaction_id": "order-10001" }'Parameters
Section titled “Parameters”| Parameter | Tier | Type | Notes |
|---|---|---|---|
moja_auth_key |
Required | query string | On the URL. Header auth is not supported |
revenue_paid_out |
Required | number | JSON number such as 150.00. A numeric string is accepted; a currency string such as "$150.00" is not |
call_log_id |
Required unless matching by phone | string or array | The preferred matcher. Also accepts callLogId, call_sid, callSid. Up to 50 values in an array |
phone_number |
Required if no call_log_id |
string | Also accepts phoneNumber. Matched against the caller ANI, not the dialed campaign number. E.164 preferred |
transaction_id |
Recommended | string | Idempotency key. One stable value per external sale, order, or conversion |
If both call_log_id and phone_number are supplied, call_log_id takes priority.
Matching behaviour
Section titled “Matching behaviour”Prefer call_log_id whenever your system can carry it. It updates exactly the call you mean.
Phone matching is the fallback. It uses the webhook’s Caller ID Match Days window (1–7 days), and if several calls match that caller ID inside the window, all of them are updated. Never use phone matching when a single sale must update exactly one call.
Timing
Section titled “Timing”Send the postback after the call is complete and the final amount is known. If it arrives before connected duration is finalized, Moja may reject it:
Cannot update revenue on a call with no connected duration.Replay the postback once the call has completed and connected duration exists. Do not assume Moja will silently retry an early postback on your behalf.
If the call’s converted_at timestamp is not already set, updating revenue sets it to the current time.
Idempotency
Section titled “Idempotency”Include a stable transaction_id per external sale. When the same organization and transaction_id are sent again after a completed attempt, Moja returns the original stored response instead of applying the request as a new event. If a request with that ID is still in flight, you may get 409 Conflict — wait, then retry with the same ID.
Server-side idempotency is best effort and can fail open on rare infrastructure errors, so make the sending system safe: one stable ID per sale, and never retry the same sale under a new ID.
Responses
Section titled “Responses”| Code | Meaning | What to do |
|---|---|---|
200 |
Accepted. Body lists the updated call_log_ids |
Mark the sale synced after checking the body, not just the status code |
401 |
Missing or invalid moja_auth_key |
Recopy the URL from Webhooks → Incoming |
409 |
A request with the same transaction_id is in flight |
Wait, then retry with the same transaction_id |
422 |
No connected duration on the call | Replay after the call completes |
429 |
Rate limited | Slow down and retry later |
Partial success
Section titled “Partial success”When updating several call logs, some can succeed while others fail. A partial response may include fields such as partial_success, failed_call_logs, or failures. Read the body before marking the external sale fully synced.
Verify it works
Section titled “Verify it works”-
Pick one completed call with a known
call_log_idand a real connected duration. -
Post a small distinctive amount against it, with a
transaction_idyou can recognise later. -
Confirm the response is
200and thatcall_log_idscontains the ID you sent. -
Open that call in reporting and confirm the revenue value changed.
-
Send the exact same request again. Confirm you get the stored original response back rather than a second update, which proves your idempotency key is working.
-
Send once with a deliberately wrong auth key and confirm
401.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Likely cause | Fix |
|---|---|---|
401 Unauthorized |
moja_auth_key missing from the URL or invalid |
Copy the exact URL from Webhooks → Incoming; do not send the key as a header |
429 Too Many Requests |
Rate limited | Slow the send rate and retry |
409 Conflict |
Same transaction_id still in flight |
Wait, then retry with the same ID |
422, no connected duration |
Postback sent before the call finished | Replay after completion |
| Call log not found | Wrong call_log_id, wrong phone format, matching against the dialed number instead of the ANI, outside the match window, or the key belongs to another organization |
Work down that list in order |
| Revenue did not change | Currency-formatted amount such as "$150.00" |
Send 150.00 as a number |
| Several calls updated from one postback | Phone matching hit multiple calls in the Caller ID Match Days window | Use call_log_id when the sale should update exactly one call |
| Duplicate revenue recorded | The same sale was retried under a new transaction_id |
Use one stable ID per sale and reuse it on every retry |
Best practices
Section titled “Best practices”- Prefer
call_log_idover phone matching. - Use phone matching only when updating every matching call in the window is acceptable.
- Send after the call is complete and connected duration exists.
- Send
revenue_paid_outas a number. - Include a stable, unique
transaction_idper sale. - Log both request and response in the external system.
- Test against a known completed call before going live.

