Webhooks for won deals and leads
RankUpside takes won deals at POST /api/hooks/revenue and calls or form leads at POST /api/hooks/leads, both authenticated with the client's revenue key as a bearer token. Send one JSON object or an array of up to 500. Re-sending an item with the same ID updates it, and each bad item gets its own error.
What it doesn't do: The webhooks don't pull from your CRM. Something has to send each deal or lead: a CRM workflow, Zapier, Make or the call tracker's own webhook.
Updated

The site key
Each client site has one revenue key, shown in Client › Settings › Leads & revenue. Both webhooks use it as a bearer token:
Authorization: Bearer rk_…
Call trackers that can only post to a URL, such as CallRail and WhatConverts, put the key in the address instead: ?key=rk_…. The settings page shows those two providers that URL form with the key masked; paste in the site's full key before you save it in the call tracker. Treat that URL like a password.
New key creates a replacement and the old key stops working right away. The new key is shown once: “Save this one now; it won't be shown again.” Update it wherever it's used, such as Zapier, Make or the CRM. Keys are stored as hashes.
Revenue webhook
POST https://rankupside.com/api/hooks/revenue
Send one deal as a JSON object, or up to 500 as an array.
| Field | Also accepted | What it takes |
|---|---|---|
id | external_id | The CRM's deal ID. Re-sending the same ID updates the deal. |
date | YYYY-MM-DD, M/D/YYYY or a written date like "Oct 14, 2026". Missing means today. | |
landing_page | landingPage, url | The page the customer first landed on: a path or a full URL. Missing means the deal shows as unmatched. |
amount | value | A non-negative number, up to 10,000,000. Dollar signs and commas are fine. |
status | won, open or lost, plus CRM words such as "closed won", "paid" or "cancelled". Lost words win over won words. Missing means won. | |
source | Where the lead came from, such as organic. Deals whose source names ads, social, a directory or the Business Profile are left out of search revenue. | |
phone, email | Optional. Used only to match the deal to a call or form lead, and stored as a salted hash. |
POST /api/hooks/revenue Authorization: Bearer rk_•••••••• { "id": "D-1042", "date": "2026-10-14", "landing_page": "https://copperlinegutters.example/", "amount": 4200, "status": "won", "source": "organic" }
How each deal is then split across searches: How revenue is allocated.
Leads webhook
POST https://rankupside.com/api/hooks/leads?provider=callrail|whatconverts|generic|quo
Calls and form leads use the same site key. Pick the provider that sent the payload; generic is the default. Each lead needs an ID, a date and a type (call or form).
| Provider | ID and date | Type | Landing page and source | Duration and contact |
|---|---|---|---|---|
| CallRail (post-call) | id, start_time | Always a call | landing_page_url; source + medium | duration, customer_phone_number, first_call, keywords |
| WhatConverts | lead_id, date_created | lead_type: "Phone Call" or "Web Form" | landing_url; lead_source + lead_medium | call_duration_seconds, phone_number, email_address |
| Generic | id, date | type: call or form | landing_page; source | duration in seconds, phone, email |
Quo (call.completed) | The call's ID and completed time | Always a call | None: business-line calls count toward totals without a page | Duration and caller number. Outbound calls are counted as skipped, not errors. |
curl -X POST https://rankupside.com/api/hooks/leads?provider=generic \
-H "Authorization: Bearer rk_…" \
-H "Content-Type: application/json" \
-d '{"id":"TEST-example","date":"2026-10-20T12:00:00Z","type":"call",
"landing_page":"https://copperlinegutters.example/services/gutter-repair",
"source":"organic","duration":30,"phone":"+1 202-555-0142"}'Calls whose source is Google Maps or the Business Profile are kept apart as Business Profile calls, not organic leads.
Responses
| Status | When |
|---|---|
| 200 | At least one item was saved, or there was nothing to save. Failed items are listed in errors. |
| 400 | Every item failed, the body isn't JSON, or the provider isn't one of the four. |
| 401 | No key in the header or the URL, or the key is unknown. |
| 413 | More than 500 items in one request. |
The revenue webhook returns {ok, inserted, updated, errors}. The leads webhook adds skipped. ok is false whenever any item failed. Each error names its item, counting from 0:
{
"ok": false,
"inserted": 2,
"updated": 1,
"errors": ["Item 3: unreadable date."]
}Recipes
- GoHighLevel: a workflow step posts each won opportunity.
- HubSpot: a workflow webhook on closed-won deals, or a CSV export.
- Zapier: Webhooks by Zapier, for CRMs without a webhook of their own.
- CallRail and WhatConverts: their own webhooks, pointed at the leads URL.
No webhook at all? Upload a file instead: CSV format for won deals.
Test it
- Copy the webhook request from Client › Settings › Leads & revenue. It already has the client's address and key ending.
- Run it with the full key, or use Send test lead under Call tracking.
- Check Recent deals or Recent leads on the same page. Test items use fictional data; give your own tests IDs starting with TEST- so they're easy to spot.
Something missing or out of date? Email hello@rankupside.com.
Questions.
Can one request send many deals?
What happens if the same deal is sent twice?
Does a deal need a landing page?
Is the caller's phone number stored?
Related
- DocsCSV formatUpload won deals from any CRM export. The required date, amount, landing page and status columns, accepted header names, and how re-uploads update deals.
- DocsForm snippetOne script tag adds landing_page and lead_source hidden fields to every form on a client's site, so won deals can be tied back to the pages and searches.
- DocsHow revenue is allocatedThe exact rule: won deal to landing page, split across that page's searches by click share, hidden searches on their own line, and what's left out.
- IntegrationWebhooksPost won deals and calls to RankUpside by webhook from any CRM, Zapier or Make. One key per client site, up to 500 items per request, per-item errors.