Skip to content
Coritan Docs

Send purchases to your ad platforms

Send purchases, renewals, sign-ups and refunds from our servers to Google Analytics 4, Google Ads, Meta, TikTok, Microsoft Ads, Reddit and X.

View as Markdown

A server-side conversion is a sale that we report to an ad platform from our servers, after the invoice is paid. The browser tags report a purchase only when the buyer's browser stays open, allows the tag and is not blocked. Reporting from the server as well counts the sales that the browser missed, and it counts refunds, which no browser sees.

You give each platform the ID and secret that let us report to your account. We then send what happens on your storefront: each paid invoice, each new account and each refund. We send nothing for a platform you leave off.

  • You need the Owner or Admin role in the organization. Members with any other role cannot read or change these settings, because they hold platform secrets.
  • You need an account on each platform you want to use, with its pixel or data source already made, and an access token or API secret for it. Set up each platform says where to find them.
  • Your storefront must be one that Coritan serves, so the visitor's tracking cookies reach us with the order. A storefront you build yourself on the Storefront API needs to forward them, as How customers and orders are attributed describes.

One event goes to each platform that is on, for each of these things that happen:

Event When it is sent Sent as
purchase An invoice is paid and a service on it was still waiting for that payment: the buyer's first purchase of that service. Google Analytics 4 purchase, Google Ads (the purchase label), Meta Purchase, TikTok CompletePayment, Reddit Purchase, X (the purchase event).
upgrade A paid invoice charges for a plan change, an add-on or a one-time line on a service that is already active. The same as a purchase.
renewal A paid invoice bills a service that is already active for another period. Off until you turn on send_renewals for the platform. Google Analytics 4 renewal, Google Ads (the renewal label), Meta, TikTok and Reddit as a custom event named Renewal, X (the renewal event).
free_server An order with nothing to pay, such as a free server, is settled. It is a lead and never a purchase. Google Analytics 4 generate_lead, Google Ads (the lead label), Meta Lead, TikTok CompleteRegistration, Reddit Lead, X (the lead event).
sign_up A customer account is made on your storefront. Google Analytics 4 sign_up, Google Ads (the sign_up label), Meta and TikTok CompleteRegistration, Reddit SignUp, X (the sign_up event).
refund A refund of a sale is booked on the invoice, for the amount that went back. Google Analytics 4 refund only.
chargeback A chargeback on a sale is lost and its money taken back. Google Analytics 4 refund only.

Nothing is sent for an invoice that reactivates a suspended service, for an invoice with nothing to pay on a service that is already active, or for an invoice with no service on it. An invoice paid before you turned a platform on is never sent to it. A refund or a lost chargeback is sent only for an invoice that was a purchase, an upgrade or a renewal when it was paid. A refund that you give as account credit is not sent, because no money went back to the buyer.

Every event for a purchase carries the same ID, purchase: followed by the invoice number, such as purchase:INV-000042. The browser tag sends the same ID, so a platform that receives the sale from both counts it once. Google Analytics 4 and Google Ads get the invoice number itself as the transaction ID. A renewal's ID starts with renewal:, a free server's with lead: and a sign-up's with sign_up:.

Meta, TikTok, Reddit and X have no event for a refund, and Google Ads can take one back only through its own API, which we do not use. Each of those platforms gets a delivery marked skipped that says so, so you can see why nothing went.

We copy the visitor's cookie choice onto the order when it is placed, and what we send depends on it. See Consent and privacy for how the choice is made.

The buyer allowed marketing
We send what the platform uses to match the sale to the ad: the SHA-256 hash of the email address, the phone number, the name and the address, the SHA-256 hash of the customer ID, the click ID the visit arrived with, the Meta browser IDs (fbp and fbc), and the IP address and browser name of the order. Reddit gets the IP address hashed.
The buyer denied marketing, or we cannot tell what they chose
We send nothing that names the buyer: no email, phone, name, address, customer ID, click ID, cookie, IP address or browser name. Meta, TikTok, Reddit and X are not sent the sale at all. Google Analytics 4 and Google Ads receive the count, the value and the invoice number only, and Google Analytics 4 sends no user ID.
The buyer denied analytics
Google Analytics 4 is not sent anything for that sale, and its client and session IDs are left out.

An invoice that did not begin as a storefront order has no choice on file: an invoice your team made, or a renewal. It uses the choice the customer made when they signed up, and when there is none, everything is denied. Nothing is ever sent as a plain email address or phone number. Meta, TikTok and X are sent the hashes they ask for, and Google Ads receives the hashed email only with marketing allowed.

Owners and admins turn each platform on in the dashboard. Your team can do the same in the staff console, as Team and settings describes.

You need the Owner or Admin role, because the page holds platform secrets. A member with another role does not see the page, and its address opens Overview. You also need, from each platform's own site, the ID and the secret that Set up each platform names.

  1. In the dashboard, open the organization, then Settings, then Conversions.
  2. Find the platform's card. It names the platform and shows a badge: On, Test mode, Off, or Failing when the last event we sent it was refused. Under the name, Last event sent says what we last sent and when, and Last 30 days says how many of your first purchases from the last 30 days we sent it.
  3. Fill in the platform's settings, such as "Measurement ID" for Google Analytics 4 or "Pixel ID" for Meta. Each hint says where to find the value. Google Ads asks for "Conversion labels" and X for "Event IDs", one for each kind of event. Set at least "Purchase".
  4. Type the secret into its field, such as "API secret" or "Access token". X takes four values: "Consumer key", "Consumer secret", "Access token" and "Access token secret". Google Ads and Microsoft Ads take no secret. After you save, the card shows Saved where the field was, because we never show a secret again. Select Replace to type a new one, or Clear to remove it.
  5. Turn on Send events. Turn on "Send renewals" to send each paid renewal as its own conversion action. Turn on Test mode to mark every event as a test while you check the setup. Google Ads, X and Microsoft Ads have no test mode, so their cards have no such switch.
  6. Select Save changes. Each card saves alone, so what you typed in another card stays unsaved until you save that card. A setting that does not fit, or a platform turned on without what it needs, shows the API's sentence under the input it belongs to, and focus moves there.
  7. For a platform with a test mode, select Send test event. It sends one made-up purchase with the settings you saved, so it is off while a card has unsaved changes. Meta and TikTok need their "Test event code" first. The card says whether the platform accepted the event. A test is not counted in Last 30 days.

When it works, the page says "Meta Conversions API turned on." or "Meta Conversions API saved.", the badge reads On and the secret reads Saved. After the next paid invoice, Last event sent shows the purchase as Sent. A Skipped event carries the reason, such as a buyer who did not allow marketing.

For Microsoft Ads, turn on Send events and select Save changes. The page then shows Copy the file address now with the whole address of your file. Copy it into a schedule in Microsoft Advertising, as Send purchases to Microsoft Advertising describes. We show the address whole only that once. Later it shows with the token hidden, next to Last fetch, which says when Microsoft Advertising last read the file. If the address leaks, select Replace address…, type replace and select Replace address. The old address stops working at once, and the page shows the new one whole.

Each platform has a kind, settings that are not secret (config) and, except for Google Ads and Microsoft Ads, one secret. The secret is write only: we seal it, never show it again, and the API answers only secret_set.

Platform kind config Secret
Google Analytics 4 ga4 measurement_id The Measurement Protocol API secret: in Google Analytics, open Admin, Data streams, your web stream, then Measurement Protocol API secrets.
Google Ads google_ads conversion_id and labels None. In Google Ads, open Goals, Conversions, then each conversion action and its tag setup: the ID starts with AW- and the label follows the slash. Make one conversion action for each of purchase, renewal, sign_up and lead that you want.
Meta meta_capi pixel_id, test_event_code The access token: in Meta Events Manager, open your data source, then Settings and Conversions API. The test event code is on its Test events tab.
TikTok tiktok_events pixel_code, test_event_code The access token: in TikTok Events Manager, open your web events, then Settings and Events API. The test event code is on its Test events view.
Reddit reddit_ads pixel_id The conversion access token: in Reddit Ads, open Events Manager and your data source, then Conversions API. pixel_id is the Pixel ID there, not the ID of the ad account.
X x_ads pixel_id and events Four OAuth values as one object: consumer_key, consumer_secret, access_token and access_token_secret, from an app in the X developer portal with Ads API access. events names the X event ID, which starts with tw-, for each of purchase, renewal, sign_up and lead.
Microsoft Ads microsoft_ads conversion_name, lookback_days, browser_grace_minutes, email_only_rows None. We make an address for the file Microsoft Advertising imports. See Send purchases to Microsoft Advertising.

Every platform also takes send_renewals, true or false, which is false until you set it.

An ID is checked as the browser tags check it, so a value that the tags accept is accepted here, and an ID you type in lower case where the platform uses capitals is saved in capitals. A Google Ads labels and an X events object replace the whole map each time. You turn a platform on with enabled: true once its settings and secret are saved.

Turn on Google Analytics 4
curl -X PUT "https://api.coritan.com/api/v1/orgs/acme/conversions/ga4" \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "secret": "AbCdEfGhIjKlMnOpQrStUv", "config": {"measurement_id": "G-ABC123DEF4"}}'
Turn on Google Ads with a purchase and a lead label
curl -X PUT "https://api.coritan.com/api/v1/orgs/acme/conversions/google_ads" \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "config": {"conversion_id": "AW-123456789", "labels": {"purchase": "AbCdEfGhIjKlMn", "lead": "OpQrStUvWxYz01"}}}'
Turn on X with its four credentials
curl -X PUT "https://api.coritan.com/api/v1/orgs/acme/conversions/x_ads" \
  -H "Authorization: Bearer $CORITAN_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"enabled": true, "config": {"pixel_id": "o1abc", "events": {"purchase": "tw-o1abc-o2def"}}, "secret": {"consumer_key": "...", "consumer_secret": "...", "access_token": "...", "access_token_secret": "..."}}'

The answer holds the saved settings and secret_set, and changed lists what this call changed. Send only the keys you want to change: a config key replaces the saved one, and null or an empty string removes it. An empty secret clears it. Saving what is already saved changes nothing and writes no audit entry. A value that does not fit answers 422 with detail, the sentence to show, and field, the input it belongs to:

JSON
{ "detail": "Meta pixel id must be 8 to 20 digits", "field": "config.pixel_id" }
  1. Send a test event with POST /conversions/{kind}/test. It sends one made-up purchase of one currency unit for a product called Test product, with nothing about a person in it. Meta and TikTok send their test event code, Google Analytics 4 sends to its validation endpoint and Reddit sends its test flag, so nothing reaches live reporting. Set test_event_code for Meta and TikTok first.
  2. Read the answer: ok is true when the platform accepted the event, and http_status and error carry what it said when it did not. A test is not logged as a delivery and does not count toward coverage.
  3. Look for the event in the platform: Meta and TikTok in their test events, Google Analytics 4 in DebugView.
  4. After the next real purchase, read GET /conversions/deliveries. A purchase that went out has a delivery with status sent.

You can turn on test_mode for a platform to send every real event as a test. Google Ads and X have no test mode, so a test would count as a real sale: their test call answers 422, and while either is in test mode nothing is sent to it. Microsoft Ads takes no events, so its test call answers 422 too.

You can send at most ten test events an hour for each platform, and the eleventh answers 429.

GET /conversions lists the seven platforms, whether or not you set them up. Each entry has these parts besides its settings:

last_delivery
The newest event the platform was sent, with its event, status, http_status, error and the time at, or null.
coverage
How many of your first purchases the platform was sent. orders is the invoices paid in the last 30 days that were a first purchase, sent is how many of them the platform's purchase was sent for, and rate is sent over orders, or null with no orders. A free server, a renewal and an upgrade are in neither number.

GET /conversions/deliveries lists every event, newest first. Filter it with kind, status (sent, failed or skipped) and invoice_number, which also finds the refunds and chargebacks booked on that invoice. limit takes 1 to 200 (50 by default) and offset pages through. Each row names its invoice, the event and the status:

sent
The platform answered with a success.
skipped
We chose not to send, and error says why. Common reasons: Renewals are off for this integration., The buyer did not allow marketing, so nothing is sent to Meta Conversions API., The buyer did not allow analytics, so nothing is sent to Google Analytics., No Google Ads conversion label is set for lead., No X event ID is set for lead., X needs a click ID or an email address to match a conversion, and this buyer gave none., and Meta Conversions API has no refund event.
failed
The platform refused the event, or did not answer. http_status and error hold what it said, with your secret taken out. A timeout, a connection failure, 408, 429 and any 5xx are tried again, three attempts in all, and the last row ends Gave up after 3 attempts. Any other answer is final at once.

A delivery that is sent or skipped is final, so a sale is never reported to a platform twice by us.

Send purchases to Microsoft Advertising

Section titled Send purchases to Microsoft Advertising

Microsoft Advertising does not take events. It fetches a file of offline conversions on a schedule, and we serve the file.

  1. In Microsoft Advertising, make an offline conversion goal and note its name.
  2. Turn Microsoft Ads on with PUT /conversions/microsoft_ads and {"enabled": true, "config": {"conversion_name": "Offline Purchase"}}. The first time, the answer's feed_url holds the whole address of your file, ending in a token. Copy it now: later answers show it with the token hidden in feed_url_masked.
  3. In Microsoft Advertising, open Conversions, Offline conversions, then Schedules, and add that address as the file to import.

The file lists a purchase when its invoice was paid in the last lookback_days (30 unless set), at least browser_grace_minutes ago (120 unless set) and after you turned Microsoft Ads on, and when the buyer allowed marketing. It lists first purchases and upgrades, and renewals when send_renewals is on. Each row holds the msclkid the buyer arrived with, your conversion_name, the time paid, the total, the currency and the hashed email. A buyer with no click ID is listed by the email alone when email_only_rows is on (the default) and the visit came from Microsoft. In test mode the file lists nothing.

The grace period gives the buyer's browser the first chance to report the sale with the UET tag from your tags. When the invoice shows paid, your storefront calls POST /storefront/conversions/microsoft/claim with the customer's token and {"invoice_id": 42}. The answer {"fire": true} comes once for each invoice, and the browser then fires the UET event and the file leaves that invoice out. Every other answer is {"fire": false}: the file already listed it, the invoice is unpaid or not a sale, or Microsoft Ads is off or in test mode. At most 10 claims an hour for one invoice and 600 an hour from one IP address.

Microsoft Advertising fetches the file with no sign-in: the token in the address is the credential. A wrong token, an organization with the integration off and an unknown organization all answer 404. Replace a lost or leaked address with POST /conversions/microsoft_ads/rotate-feed-token. The answer holds the new address, and the old one answers 404 from then on. Purchases the file already listed stay listed. Rotating before you first turned Microsoft Ads on answers 409.

After a customer pays, we queue the purchase in the same transaction that records the payment and send it from a background job. GET /conversions/deliveries then lists one row for each platform you turned on, with its status. The invoice number is the same in your platform's reports and in your invoices.

A delivery is skipped with The buyer did not allow marketing
Meta, TikTok, Reddit and X are sent a sale only when the buyer allowed marketing. Google Analytics 4 and Google Ads still receive the count. The browser tags follow the same choice.
A delivery is failed with HTTP 400 or HTTP 401
The platform refused the ID or the secret. Check the ID against the platform, make a new access token and save it again. The failure is final for that sale.
Meta or TikTok answers a test with 422 and Test mode sends a test event code
Set config.test_event_code from the platform's test events page, then test again.
A Google Ads purchase shows no conversion
We send Google Ads the click ID only when the buyer allowed marketing and the visit had one. Without it the request carries the invoice number, the value and the currency alone. Check that the purchase label belongs to the conversion action you read, and read the delivery's status first.
The same sale counts twice
The browser tag and the server send the same event ID, so Meta, TikTok, Reddit and X count it once. For Google Analytics 4 and Google Ads, make sure the tag sends the invoice number as the transaction ID, which the storefront's tags do.
Microsoft Advertising says the file is not found
The address needs its token, and a rotated address replaces the old one. Turn Microsoft Ads on, copy feed_url from that answer, or rotate to get a new one.
coverage.rate is lower than you expect
Count only first purchases paid in the last 30 days. A purchase whose buyer denied marketing is skipped for the platforms that need it, and a platform you turned on after the sale never receives it.

The staff console's routes under /api/v1/orgs/{org_slug}/staff/marketing/conversions answer the same, for the Admin and Owner roles, and platform staff have the same routes for any organization in the admin console. All of them need an access token, sent as Authorization: Bearer $CORITAN_TOKEN.

Operation What it does
GET /conversions The seven platforms with their settings, secret_set, last_delivery and coverage.
PUT /conversions/{kind} Turn a platform on or off, set test_mode, change its config, set or clear its secret.
POST /conversions/{kind}/test Send one made-up purchase in test mode.
GET /conversions/deliveries The delivery log, filtered by kind, status and invoice_number.
POST /conversions/microsoft_ads/rotate-feed-token A new address for the Microsoft Ads file.
GET /conversions/microsoft/feed The file Microsoft Advertising fetches. No sign-in, a token query.
POST /storefront/conversions/microsoft/claim Whether the thank-you page should fire the UET event. The customer's token.

Every change is recorded in the audit log as conversions.updated (the settings that changed, never a secret), conversions.test_sent or conversions.feed_rotated.

API operations on this page

MethodPathWhat it does
GET/api/v1/orgs/{org_slug}/conversionsList the ad platforms purchases are sent to, and how each is doing
GET/api/v1/orgs/{org_slug}/conversions/deliveriesRead what each platform was sent, and why anything was not
PUT/api/v1/orgs/{org_slug}/conversions/{kind}Turn an ad platform on or off and change its settings
POST/api/v1/orgs/{org_slug}/conversions/{kind}/testSend a made-up purchase to an ad platform in test mode
POST/api/v1/orgs/{org_slug}/conversions/{kind}/rotate-feed-tokenReplace the token in the Microsoft Ads file address
GET/api/v1/orgs/{org_slug}/conversions/microsoft/feedFetch the offline conversions file Microsoft Advertising imports
POST/api/v1/orgs/{org_slug}/storefront/conversions/microsoft/claimAsk whether the thank-you page should report a purchase to Microsoft Ads
GET/api/v1/orgs/{org_slug}/staff/marketing/conversionsList the ad platforms purchases are sent to, and how each is doing
GET/api/v1/orgs/{org_slug}/staff/marketing/conversions/deliveriesRead what each platform was sent, and why anything was not
PUT/api/v1/orgs/{org_slug}/staff/marketing/conversions/{kind}Turn an ad platform on or off and change its settings
POST/api/v1/orgs/{org_slug}/staff/marketing/conversions/{kind}/testSend a made-up purchase to an ad platform in test mode
POST/api/v1/orgs/{org_slug}/staff/marketing/conversions/{kind}/rotate-feed-tokenReplace the token in the Microsoft Ads file address