Documentation

EdgeConversions guide

Everything you need to send reliable conversion data to Meta and Google — written for marketers first, with developer details where needed.

How it works

Ad platforms optimise campaigns using the conversions they receive. Browser pixels lose a large share of them to ad blockers, Safari's tracking prevention and cookie limits. EdgeConversions sends the same events from a server instead, so far fewer get lost.

Your website

script or your server

EdgeConversions

cleans, hashes, retries

Meta · GA4

→ Google Ads via GA4

Quick start

  1. Add a website — Workspace → “Add a website”: a name and your main domain.
  2. Connect destinations — Destinations tab: Meta Dataset ID + access token and/or GA4 Measurement ID + API secret.
  3. Send a test event — Press “Send test event”. With a Meta test code filled in, it appears in Events Manager → Test events.
  4. Connect your subdomain (recommended) — see below.
  5. Install the script — copy the snippet from the Install tab into your site's <head>.

Install the script

Paste the snippet into the <head> of every page (in Shopify, WordPress or Google Tag Manager, use a “custom HTML” block). Your real snippet, with your own address, is on the website's Install tab.

<script>window.abp=window.abp||function(){(abp.q=abp.q||[]).push(arguments)};</script>
<script async src="https://t.yourstore.com/r/main.js"></script>

A PageView is sent automatically. Call abp('track', …) for conversions:

// 1. Optional: tell us who the visitor is (hashed before sending)
abp('identify', { email: '[email protected]', phone: '08123456789' });

// 2. Track a conversion
abp('track', 'Purchase',
  { value: 250000, currency: 'IDR', transaction_id: 'order-1001' },
  { event_id: 'order-1001' });

// Turn off the automatic PageView:
// <script async src="..." data-pageview="off"></script>

Already using the Meta Pixel? Pass the same event_id to both. Meta then counts the conversion once instead of twice (deduplication).

Events & parameters

Use Meta's standard names; we translate them for GA4. Custom names (e.g. BookDemo) are allowed and become snake_case in GA4 (book_demo).

You send (Meta) GA4 receives
PageViewpage_view
ViewContentview_item
Searchsearch
AddToCartadd_to_cart
AddToWishlistadd_to_wishlist
InitiateCheckoutbegin_checkout
AddPaymentInfoadd_payment_info
Purchasepurchase
Leadgenerate_lead
CompleteRegistrationsign_up
Subscribesubscribe
StartTrialstart_trial
Contactcontact

Event data (3rd argument)

  • value, currency — order value, e.g. 250000 and "IDR"
  • transaction_id — your order number
  • items — GA4-style list; converted to Meta contents automatically

Customer data (identify / user)

email, phone, first_name, last_name, city, state, zip, country, external_id. All are hashed (SHA-256) on our server. Indonesian phone numbers like 0812… are normalised to 62812….

Your own subdomain

Serving the script from a subdomain of your store (e.g. t.yourstore.com) makes requests first-party: ad blockers rarely block them and cookies set by the server last up to 400 days instead of Safari's 7.

  1. Install tab → enter the subdomain → Connect.
  2. At your DNS provider add: CNAME t → t.edgeconversions.com
  3. Cloudflare users: set the record to “DNS only” (grey cloud).
  4. Wait 1–5 minutes until DNS and HTTPS both show Active, then copy the updated snippet.

Server API

Send conversions that happen outside the browser — paid orders, payment webhooks, CRM stage changes. Authenticate with the website's secret key (API tab). Never put the secret key in browser code.

POST https://edgeconversions.com/api/v1/events
Authorization: Bearer sk_your_secret_key
Content-Type: application/json

{
  "events": [{
    "event_name": "Purchase",
    "event_id": "order-1001",
    "event_time": 1767225600,
    "url": "https://yourstore.com/thanks",
    "ip": "203.0.113.10",
    "user_agent": "Mozilla/5.0 ...",
    "user": { "email": "[email protected]", "phone": "08123456789" },
    "data": { "value": 250000, "currency": "IDR", "transaction_id": "order-1001" }
  }]
}
HTTP/1.1 202 Accepted
{ "accepted": 1, "dropped": 0, "errors": [] }
FieldNotes
event_name Required. Letters, digits, _ (max 64).
event_id Recommended. Same id = counted once by Meta.
event_time Unix seconds; within the last 7 days. Defaults to now.
ip, user_agent The customer's, not your server's — improves matching.
action_source website · app · email · phone_call · chat · physical_store · system_generated · other

Limits: 100 events per request. Responses: 202 accepted · 400 invalid body · 401 wrong key · 429 monthly quota used up (accepted events are still processed).

Mobile apps (iOS & Android)

There is no native SDK yet. Mobile apps can send business events (Purchase, Lead, signup) through the Server API. Never embed the secret key in the app: have the app call your own backend, and let the backend call the Server API.

  1. Your backend receives the event from the app and adds the user's email, phone and your own user ID as user.external_id.
  2. Send it to /api/v1/events with action_source set to app (see the payload below).
{
  "events": [{
    "event_name": "Purchase",
    "event_id": "order-1001",
    "action_source": "app",
    "user": { "external_id": "user-42", "email": "[email protected]", "phone": "08123456789" },
    "data": { "value": 250000, "currency": "IDR", "transaction_id": "order-1001" }
  }]
}
// Swift — call YOUR backend, which forwards to the Server API
var req = URLRequest(url: URL(string: "https://api.yourapp.com/track")!)
req.httpMethod = "POST"
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
req.httpBody = try? JSONSerialization.data(withJSONObject: [
  "event_name": "Purchase",
  "event_id": "order-1001",
  "value": 250000,
  "currency": "IDR"
])
URLSession.shared.dataTask(with: req).resume()
// Kotlin — call YOUR backend, which forwards to the Server API
val body = JSONObject()
  .put("event_name", "Purchase")
  .put("event_id", "order-1001")
  .put("value", 250000)
  .put("currency", "IDR")
  .toString()
  .toRequestBody("application/json".toMediaType())
val req = Request.Builder().url("https://api.yourapp.com/track").post(body).build()
OkHttpClient().newCall(req).enqueue(object : Callback {
  override fun onFailure(call: Call, e: IOException) {}
  override fun onResponse(call: Call, response: Response) { response.close() }
})

Current limits. Matching relies on hashed email, phone and external_id. Device identifiers (IDFA / GAID), SKAdNetwork and app-install attribution are not supported; use the ad platform's SDK or a mobile measurement partner (AppsFlyer, Adjust) for those. Meta may require app-specific fields for action_source app, and GA4 app streams (Firebase) are not supported yet, so check Test events before relying on it.

TikTok, Google Ads & online stores

Every event you send goes to all connected destinations. Connect them in your website's Destinations tab.

  • TikTok Events API — pixel code + access token from TikTok Events Manager. Conversions only; page views stay with the TikTok pixel. Lead is sent as SubmitForm.
  • Google Ads — connect with Google, enter your customer ID and map event names to conversion action IDs (type: import from clicks). We upload through Google's Data Manager API with the gclid and hashed email/phone (enhanced conversions), so conversions match even without a click ID.
  • WordPress & WooCommerce — install the plugin from the Developer tab. It adds the tag, tracks product views, add-to-cart and checkout, and sends each paid order server-side exactly once — with the click IDs captured at checkout.
  • Shopify — add the custom pixel (Settings → Customer events) for browsing events and an “Order payment” webhook for Purchase. The pixel links each checkout to the visitor, so the webhook's Purchase carries their fbc, ttclid and gclid. Purchases are never sent twice.

Consent, bots & cookies

Consent Mode v2. In Settings choose Basic (nothing is sent until the visitor decides) or Advanced (events are sent with the consent state and routed per destination). We read gtag('consent', …) from your cookie banner automatically, or call:

abp('consent', { ad_storage: 'granted', analytics_storage: 'granted', ad_user_data: 'granted', ad_personalization: 'granted' });

Meta, TikTok and Google Ads require ad consent; GA4 requires analytics consent.

Bots & internal traffic. Crawlers, uptime monitors and headless browsers are dropped before they count toward your quota. Add office or agency IPs (or CIDR ranges) to Blocked IPs.

Cookie Keeper. Safari and ad blockers delete cookies early. We remember each visitor's click IDs (fbclid, gclid, ttclid) and ad cookies on the server and restore them on later visits — as long as either the first-party cookie or the browser's storage survives — within each platform's attribution window.

Match quality. Each delivery to an ad platform gets an estimated 0–10 score based on the customer data it carries (email, phone, click ID, …). It is our estimate, not Meta's official Event Match Quality.

Reading the dashboard

Event vs delivery
An event is one action (a Purchase). A delivery is that event sent to one destination. With Meta and GA4 connected, 1 event = 2 deliveries. Your quota counts events.
Statuses
Delivered accepted by the platform. Retrying temporary problem; retried up to 5 times (30 s → 4 min). Failed rejected; the Response column says why.
Success rate & health
Delivered ÷ (delivered + failed). Healthy ≥ 95%, needs attention ≥ 80%, failing below 80%.
History
Delivery logs are kept for 7 days. Times are shown in UTC.

Troubleshooting

SymptomWhat to do
Meta: “Invalid OAuth access token” Generate a new token in Events Manager and save it on the Destinations tab.
Nothing in the Event log Check the snippet is in <head>, and that the page's domain is listed in Settings → Allowed origins.
Test event not visible in Meta Fill in the Test event code; it only shows under Test events.
HTTPS stays “Waiting” Turn off the Cloudflare proxy (DNS only) and wait a few minutes.
Conversions counted twice Send the same event_id from the Pixel and EdgeConversions.

Glossary

CAPI
Meta Conversions API — Meta's way to receive events from a server.
First-party
Served from your own domain, so browsers treat it as part of your site.
Event Match Quality
Meta's score for how well an event can be tied to a person. More customer data (hashed) = higher score.
Deduplication
Counting an event once when it arrives from both browser and server, matched by event_id.

Still stuck? Contact us — we're happy to help with setup.