← All guides
Send your dataUpdated ·8 min read

Send orders and events from your backend

For custom stacks: create a workspace API key and send purchases, leads and signups to the ingest API, with the visitor_id join that recovers browser identifiers for maximum match quality.


Page activity arrives through the tracking script on its own. The conversions that actually move your campaigns (purchases, leads, signups) need a connection from wherever they happen. If you run Shopify or WooCommerce, the dedicated app and plugin handle this for you with no code. This article is the reference for everyone else: one authenticated request per event, sent from your own backend.

Create an API key

On the Setup & API keys page, use Create API keyand name it after the system that will use it (“Shopify webhook”, “checkout backend”). The key is displayed once, so copy it immediately. Keys are scoped to the workspace, can be revoked individually, and the list shows when each was last used.

The request

curl -X POST https://app.roasproof.com/api/v1/events \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event": "purchase",
    "event_id": "purchase_10023",
    "value": 249.00,
    "currency": "EUR",
    "url": "https://mystore.com/checkout/thank-you",
    "occurred_at": "2026-07-03T09:41:00Z",
    "user_data": {
      "email": "[email protected]",
      "phone": "+359881234567",
      "visitor_id": "value of the _sgnl_vid cookie",
      "client_ip_address": "203.0.113.7",
      "client_user_agent": "Mozilla/5.0 …"
    },
    "custom_data": { "order_number": "10023" }
  }'

A successful request returns 202 Accepted with one entry per active destination the event was queued for. Delivery is asynchronous, with retries. Check Conversion events for per-destination status.

Field reference

  • event (required): one of the eight standard events (page_view, view_content, add_to_cart, initiate_checkout, lead, signup, purchase, subscribe) or a custom key you defined for your workspace under Events.
  • event_id: make it deterministic, derived from your order or lead ID (purchase_10023). The same ID must be carried by the browser pixel's copy of the event, so platforms can deduplicate the two. If omitted, a random ID is generated (fine for events that have no browser counterpart).
  • value / currency: order value and 3-letter currency code.
  • occurred_at: when the conversion actually happened (ISO 8601); defaults to arrival time.
  • url / action_source: the page where the conversion happened, and the source (website by default; also app, phone_call, email, physical_store, …).
  • site_key: ties the event to a specific website when the workspace has several and no visitor_id is provided.
  • custom_data: any additional properties, forwarded with the event.

user_data: send everything you have

Matching keys are what connect the conversion to a real ad click and a real person. Send raw or pre-hashed: emails and phone numbers are normalized and SHA-256 hashed on arrival, so plaintext PII is never stored:

  • email, phone: hashed on arrival
  • external_id: your customer ID, hashed on arrival
  • visitor_id: the _sgnl_vid cookie value (see below)
  • client_ip_address, client_user_agent: from the original request
  • fbc, fbp, fbclid, gclid, wbraid, gbraid, ttclid: if your backend has them

Each platform receives only the identifiers it understands: Meta gets fbc/fbp and hashed identifiers, Google gets click IDs and hashed email/phone, TikTok gets ttclid and hashed identifiers.

The visitor_id join

This one field does the heavy lifting. Pass the value of the _sgnl_vid cookie (set by the tracking script) as user_data.visitor_id, and the identifiers captured in the browser (fbc/fbp, click IDs, IP, user agent, hashed email) are merged into the outgoing conversion automatically. Values you submit explicitly always win; the visitor record only fills the gaps.

In practice: your checkout controller reads the cookie from the request and forwards it with the order. That is the difference between a purchase Google can't attribute and one that carries the original gclid from a click three weeks ago.

Limits and errors

  • 202: accepted and queued per destination.
  • 422: validation failed; the response names the offending field.
  • 429: the tracked-conversion ceiling was reached. Conversions past your plan's cap are normally still accepted and billed as metered overage, never dropped silently. This response only fires if there is no active subscription or trial to bill the overage to, or once usage reaches 3x the plan cap regardless of billing status (an abuse safeguard, not something normal usage hits).

Only billable conversions count against your plan (purchase, lead, signup, subscribe, or a custom event you marked billable). Funnel events like page_view and add_to_cart are unmetered, and one conversion sent to Meta, Google and TikTok still counts as one tracked event against your plan quota, not three.

Ready to see your own events flowing?

Connect your store, verify your events end-to-end, and watch match quality climb. Free 14-day trial, no credit card required.