Conversion signals

Conversion APIs match a server-side event back to the person who saw your ad. The more identity you supply, the higher the match rate. Your app hands Ranksy those match keys once per merchant session, and Ranksy forwards them to Meta's Conversions API — with more conversion APIs to follow.


POST/api/v1/apps/:app/capi

Send conversion signals

Store the latest identity and session match keys for one merchant. Send raw values — Ranksy hashes them per platform at send time. Call it once per merchant session; every page load is fine, because newer signals replace older ones and blank fields never overwrite stored ones.

Requires the capi:write scope. That scope is separate from crm:write on purpose: forwarding merchant PII to ad platforms is a different grant from filling your CRM, so you can revoke one without the other.

Path parameter

  • Name
    app
    Type
    string
    Description

    The team app's ULID, e.g. 01JN4HKQX0000000000000000.

Request body

  • Name
    myshopify_domain
    Type
    string
    Description

    The merchant's .myshopify.com domain.

  • Name
    occurred_at
    Type
    string
    Description

    ISO-8601 time these signals were observed. Defaults to now. Ranksy uses it to reject out-of-order payloads — see Freshness.

  • Name
    user
    Type
    object
    Description

    Durable identity: email, phone, first_name, last_name, zip, city, region, country. All optional.

  • Name
    context
    Type
    object
    Description

    Session state: ip and user_agent, as observed on the merchant's own request to your backend. All optional.

Request

POST
/api/v1/apps/:app/capi
curl -X POST https://ranksyapp.com/api/v1/apps/01JN4HKQX0000000000000000/capi \
  -H "Authorization: Bearer rk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "myshopify_domain": "acme-supplies.myshopify.com",
    "occurred_at": "2026-07-23T14:36:00Z",
    "user": {
      "email": "[email protected]",
      "phone": "+14155551234",
      "first_name": "Sarah",
      "last_name": "Chen",
      "zip": "94107",
      "city": "San Francisco",
      "region": "California",
      "country": "United States"
    },
    "context": {
      "ip": "203.0.113.42",
      "user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ..."
    }
  }'

200 — Success

{
  "accepted": true,
  "shop_id": 68134502401,
  "stored": ["email", "phone", "zip", "ip", "user_agent"]
}

stored lists the fields this call actually wrote, using your own key names. A payload ignored as stale returns "accepted": true with an empty stored.


Send raw values

Do not pre-hash anything. Each conversion API normalizes differently before hashing. Meta, for example, strips non-ASCII bytes from city names rather than transliterating them, so São Paulo hashes as sopaulo. Ranksy applies the correct normalization per platform at send time. A pre-hashed value cannot be normalized, so it will not match.

Freshness

Ranksy keeps the latest known signals per merchant, under two rules:

  • Newest wins. A payload whose occurred_at is older than the stored value is ignored, so an out-of-order request cannot regress a fresher IP.
  • Blank never overwrites. An omitted or empty field leaves the stored value untouched. This makes the endpoint safe to call on every page load: send only what you have, and a session where you cannot resolve the merchant's email does not erase the email you sent yesterday.

Why there are no click ID fields

There is no fbclid, fbp, or rdt_cid parameter, by design. A merchant who clicks your ad reaches the App Store listing, installs through Shopify's OAuth flow, and lands in an embedded admin iframe where third-party cookies are partitioned. The browser context that held the click ID is gone by the time the conversion happens. The app cannot read it, so asking for it would only invite bad data.

Matching runs instead on external_id — the shop's domain and numeric Shopify ID, which collides with the identifier Shopify's own App Store pixel emits — plus the hashed identity you send here.

Errors

Beyond the universal 401 / 403 / 422 / 429 (see Errors):

  • Name
    404 — shop_not_identified
    Type
    invalid_request_error
    Description

    No installation exists for this domain on this app. Call /identify first.

  • Name
    422 — validation_failed
    Type
    invalid_request_error
    Description

    Most often a malformed email or ip.

Rate limits

Standard rate limits apply: 120 requests/min, 50,000 requests/day per key.

Your data responsibility

You are sending your merchants' personal data to third-party advertising platforms. The legal basis for that — consent, disclosure, and a data processing agreement where required — is your obligation to your merchants, not Ranksy's.

Was this page helpful?