Integrations
Connect RevKlik to your payment gateways, App Store Connect, and Google Search Console, plus sharing, email reports, and team access.
The Integrations view (top-bar tab) has four tabs: Payments, Apps, Search, and Imports.
Payments
RevKlik books revenue from these gateways. Xendit, DOKU, Mayar, and Paywuz serve Indonesian merchants. Polar serves merchants who sell worldwide.
| Gateway | Region |
|---|---|
| Xendit | Indonesia |
| DOKU | Indonesia |
| Mayar | Indonesia |
| Paywuz | Indonesia |
| Polar | Global |
Webhook endpoints for Midtrans, Stripe, and Lemon Squeezy exist, but their cards are not shown in the dashboard yet, so they are not listed as supported.
Gateway integrations are webhook-driven: copy the RevKlik webhook URL shown on the gateway’s card and paste it into the provider’s webhook settings, then store the signing key. Revenue lands in your revenue panels automatically. Polar has its own panel, described below. If a gateway has no card in your dashboard yet, report its sales with the server events API.
Server events API
For purchases a webhook cannot reach (App Store notifications, a backend of your own), report them from your server:
curl -X POST https://<your-revklik-host>/api/v1/events/server \
-H "Authorization: Bearer rv_..." \
-H "Content-Type: application/json" \
-d '{
"site_id": "<site id>",
"name": "purchase",
"revenue": 9.99,
"currency": "USD",
"external_id": "<transaction id>",
"source": "app_store",
"props": { "product": "pro", "country": "ID", "environment": "Production" },
"occurred_at": "2026-10-24T09:30:00Z"
}'Use a site token (rv_) with the events:write scope. revenue is in major
units; send a negative number for a refund, under its own key such as
<transaction id>:refund. The call is idempotent on external_id: a repeat
returns "duplicate": true and books nothing. Pass visitor_id (the
_kp_vid value from the browser) or session_id (_kp_sid) to attribute the
sale to that visitor’s source and campaign. A revenue of 0 is stored as a
plain event, not a conversion.
Rail, product and net revenue
Three optional fields feed the cross-product portfolio view:
| Field | Meaning |
|---|---|
rail | Payment rail, lowercase: apple, google_play, polar, doku, pakasir, stripe, lemonsqueezy, midtrans, xendit, mayar, paywuz, manual. Defaults from source (app_store* → apple, google* → google_play, polar* → polar, …), else the source itself. |
product | Product slug, up to 80 characters. Defaults to props.product, then props.app. |
net_revenue | What you keep, in the same currency and sign as revenue, never larger in size. Send it only when the source knows the real proceeds (for example Apple’s developer proceeds). |
Without net_revenue, RevKlik computes the net when the event is stored: the
fee is |revenue| × percent + fixed amount (the fixed amount is converted into
the event currency), and net = max(0, |revenue| − fee) with the sign of
revenue. A refund (<id>:refund) without net_revenue keeps the net/gross
ratio of the original sale. Each event records how its net was obtained
(reported, rule or refund_ratio). Nets are stored at ingest and never
recomputed when you edit a rule later. Replaying a sale with net_revenue
(for example re-running a Polar import) upgrades a stored rule estimate to
the reported figure.
Polar orders carry Polar’s own fee (platform_fee_amount); the webhook and the
order import store net_amount − platform_fee_amount as the reported net, and
fall back to the 4% + 0.40 USD rule when Polar sends no fee. A sale the buyer’s browser
already reported on the thank-you page is not booked a second time: the
confirmed order (webhook or import) adopts the nearest browser row of the same
site, rail and currency within 10 minutes, whatever amount the browser sent
(it reports the list price, the order what was paid), and fills in that row’s
order id, amount, rail and net. If the order names its buyer
(revklik_visitor_id) only that buyer’s row qualifies. A browser row that
arrives after its order is stored as duplicate. The same visitor firing the
same conversion on the same page again within a minute is stored but excluded as
a duplicate. For a rail with confirmed orders, scripts/import-polar-orders.ts
also marks browser rows in the covered period that no order claims as
unconfirmed.
Built-in fees (editable per rail on the Portfolio page, or GET/PUT /api/fee-rules with a dashboard session): apple 15%, google_play 15%,
polar 4% + 0.40 USD, stripe 2.9% + 0.30 USD, doku 0.7%, pakasir 0.7%,
every other rail 0%. A stored sale with no net is never reported as net = gross:
the portfolio computes its net from your fee rule for its rail when you read
it, and reports how many rows were estimated that way (estimated_rows,
net_source: "estimated"). bun scripts/backfill-revenue.ts (in apps/app)
stores those nets, rails and exclusions on old rows.
Test sales
A sale whose source or rail names a test (tracking_smoketest,
test_checkout, …), or whose props._test is true, is stored with
excluded_reason = "test" and left out of every dashboard, API and MCP number,
like bot traffic. Double-booked sales are excluded the same way with
excluded_reason = "duplicate", and browser-reported sales no provider order
confirms with "unconfirmed".
Portfolio revenue
curl "https://<your-revklik-host>/api/v1/portfolio/revenue?from=2026-10-01&to=2026-10-31¤cy=IDR" \
-H "Authorization: Bearer rvt_..."Needs an account token (rvt_) with analytics:read; a site token only sees
its own site. It covers every site you own or have accepted a team seat on,
excludes bot traffic, and converts each sale at today’s rates into currency
(default: your most common site currency). from and to are inclusive UTC
days (default: the last 30 days). The data object holds:
currency,from,tototals:gross,net,sales(revenue > 0),refunds(revenue < 0),refund_amount,estimated_rows(rows whose net came from the fee rule at read time)by_site,by_product,by_rail,daily:gross,netandsalesper group (by_railalso hasestimated_rows; a row without a rail takes the rail its payment provider implies, e.g.app_store_sales_report→apple)recent: the 20 newest revenue events withrevenue,net_revenue,net_source,currency,converted_gross,converted_netandoccurred_at
The same data is available to AI agents as the portfolio_revenue MCP tool.
Polar
Each site has its own Polar endpoint. In the dashboard open Integrations → Payments → Polar and follow the panel:
- In Polar open Settings → Webhooks → Add Endpoint, paste the URL the
panel shows (
https://app.revklik.com/api/webhooks/polar?site_id=<your site id>) and leave Format on Raw. - Tick
order.paidandorder.refunded. Subscription renewals are orders too, so no subscription events are needed. - Copy the endpoint’s secret into Webhook secret and save. The secret is write-only: RevKlik reports that it is saved, never its value.
Deliveries are verified with Standard Webhooks (both the whsec_ base64 form
and Polar’s older UTF-8 secret form), per site: another site’s secret, RevKlik’s
own billing secret and unsigned requests are rejected. The panel lists the
last deliveries and flags a signature mismatch.
Put revklik_visitor_id and revklik_session_id in the checkout metadata to
attribute the sale to the visitor. RevKlik books each order once, using the
order’s net amount (after discounts, before tax) and Polar’s own fee. A refund
is booked on the day it happened (the order.refunded event time), not on the
order’s date. Polar reports a cumulative refunded total, so the first refund is
one negative <order id>:refund row and each later refund adds a row for the
amount the total rose by (<order id>~<cents already refunded>:refund).
Optionally save an Organization Access Token (Settings → Access Tokens,
scopes orders:read and refunds:read). It enables Sync orders now (last 30
days, 12 months or all time, up to 50 new orders per run) and an hourly check of
the last 14 days that catches missed webhooks, refunds and Polar’s real fee.
With refunds:read each refund is dated by Polar’s own refund record; without
it the order’s last-modified time stands in.
Bot filtering
Traffic that is not a person is stored but flagged, and excluded from every
dashboard, API and MCP number. It is flagged when the browser reports
automation (navigator.webdriver), when Cloudflare Bot Management scores the
request as a bot, when it comes from a datacenter-only city, or when a session
behaves in ways a person cannot: several scroll depths in the same
millisecond within a few seconds of loading with no click, bursts of pageviews,
or more than 100 clicks. An hourly job re-judges recent sessions, so a rule
change reclassifies old data too.
Session duration counts each gap between two events up to 30 minutes, so a tab
left open overnight no longer inflates the average. Traffic sources are merged
by name: bing, bing.com and www.bing.com are one source, and only
pageviews from outside your own domain count as a source.
Apps
App Store Connect syncs App Store downloads, proceeds, and price changes
into the same view as your web revenue, so a mobile app and its website share
one dashboard. Create an App Store Connect API key (Users and Access →
Integrations), then enter the Issuer ID, Key ID, the .p8 private key, the
app’s Apple ID, and your vendor number. Apple shows the private key once, so
keep the file. The same sales can also arrive through the
server events API when you prefer to report them from
your own backend.
Search
Google Search Console imports search performance: authorize with Google, then Sync now to pull clicks, impressions, CTR, and position per query. The panel shows 30-day totals and a per-query table.
Imports
Bring history in without waiting for it to accumulate:
- Polar: connect the site from Integrations → Payments → Polar (see Polar). With an access token saved, Sync orders now brings in existing orders, their fees and refunds.
- OneDollarStats: import visitors, pages, countries, and sources with an API key and the site domain as registered there.
Share and access
The Share tab bundles everything public:
-
Public dashboard: a read-only link to your analytics; disable it any time.
-
Live widget: an embeddable live-visitor bubble or badge:
<script async src="https://app.revklik.com/klikapa-live.js" data-widget-token="YOUR_TOKEN" ></script> -
API tokens: account- or site-scoped tokens with granular scopes (
sites:read,analytics:read,analytics:export,tokens:read,tokens:write). Limits depend on your plan; see billing. The same tokens also work over MCP, so coding agents can read your analytics directly.
Email reports
In Settings → Email reports, schedule a recurring summary to your account email: daily, weekly, or monthly. Reports can be toggled on and off and show when they were last sent.
Team
In Settings → Team, invite teammates by email as Admin, Editor, or Viewer. Invitees need an existing revklik account; owners can change roles and remove members at any time.