Pixels

A merchant pixels record stores the per-merchant configuration for ad-network tracking pixels and server-side conversion APIs (CAPI). One row per merchant. The shape mirrors what Storlaunch's storefront snippet needs to fire client-side tracking, plus the secrets needed for Ripllo to make server-side conversion calls on the merchant's behalf.

This resource has two access modes you need to keep straight:

  • GET / and PATCH / — auth-required. Returns the full record including CAPI access tokens. Used by the dashboard.
  • GET /public/:accountId — no auth. Returns IDs only, never secrets. Used by Storlaunch's storefront pages to fire client-side pixel snippets.

The explicit select list on the public route is the primary guard against shipping metaCapiAccessToken to the storefront. Any future edit to this resource must be paired-reviewed with the same rigor as a payout webhook secret.

Endpoints

Method Path Purpose
GET /api/v1/pixels Get pixel config
PATCH /api/v1/pixels Update pixel config
GET /api/v1/pixels/public/:accountId Public pixel config (no auth, no secrets)

Get pixel config

GET /api/v1/pixels

Returns the merchant's full pixel record, including secrets. If the merchant hasn't configured anything yet, returns a default stub with enabled: true and every provider field null, so the dashboard can render the empty state without special-casing.

Response

{
  "data": {
    "id": "mxp_01HX...",
    "accountId": "acc_01HX...",
    "metaPixelId": "1234567890",
    "metaCapiAccessToken": "EAAGm…",
    "metaTestEventCode": "TEST12345",
    "googleAnalyticsId": "G-AB12CD34EF",
    "googleAdsConversionId": "AW-1234567890",
    "googleAdsPurchaseLabel": "abcDEF1234",
    "tiktokPixelId": "C4ABC123…",
    "enabled": true,
    "createdAt": "2026-05-01T10:42:00.000Z",
    "updatedAt": "2026-05-13T10:11:00.000Z"
  },
  "error": null,
  "meta": { "requestId": "...", "timestamp": "..." }
}
Status error.code When
403 NO_ACCOUNT Caller's token has no accountId.

Update pixel config

PATCH /api/v1/pixels

Upserts the merchant's record. Only fields you supply are updated; omitted fields stay as-is. To clear a field, send null.

Request body

Field Type Notes
metaPixelId string (≤64) | null The Meta Pixel ID, e.g. 1234567890. Safe to embed client-side.
metaCapiAccessToken string (≤500) | null The CAPI access token, used server-side by Ripllo to send conversion events to Meta's Conversions API. Never returned by the public endpoint.
metaTestEventCode string (≤64) | null Meta's test-event code for CAPI dry runs. Set during integration testing; clear once verified.
googleAnalyticsId string (≤64) | null Measurement ID, format G-XXXXXXXX.
googleAdsConversionId string (≤64) | null Google Ads conversion ID, format AW-XXXXXXXXXX.
googleAdsPurchaseLabel string (≤64) | null Google Ads conversion label for purchases.
tiktokPixelId string (≤64) | null TikTok Pixel ID.
enabled boolean Master switch — when false, the public endpoint returns null and Ripllo's server-side CAPI dispatcher skips this merchant.

Response

The full updated MerchantPixels object (including secrets, since this is the merchant-facing endpoint).

Examples

// Node
await ripllo.pixels.update({
  metaPixelId: '1234567890',
  metaCapiAccessToken: process.env.META_CAPI_TOKEN,
  googleAnalyticsId: 'G-AB12CD34EF',
  enabled: true,
});
# Python
ripllo.pixels.update(
    meta_pixel_id='1234567890',
    meta_capi_access_token=os.environ['META_CAPI_TOKEN'],
    google_analytics_id='G-AB12CD34EF',
    enabled=True,
)
// Go
_, err := client.Pixels.Update(ctx, &ripllo.PixelsUpdateParams{
    MetaPixelID:          ripllo.String("1234567890"),
    MetaCapiAccessToken:  ripllo.String(os.Getenv("META_CAPI_TOKEN")),
    GoogleAnalyticsID:    ripllo.String("G-AB12CD34EF"),
    Enabled:              ripllo.Bool(true),
})
# curl
ripllo_curl PATCH '/api/v1/pixels' \
  '{"metaPixelId":"1234567890","enabled":true}'

Public pixel config

GET /api/v1/pixels/public/:accountId

No auth. Returns a sanitized view of the merchant's pixel IDs, suitable for shipping to a storefront page. Returns null (not 404) when:

  • The merchant has no config row.
  • The merchant has set enabled: false.
  • The merchant has a row but every IDs field is empty (i.e., nothing to fire).

This null-pass-through is what lets storefronts unconditionally request the snippet and skip rendering when it comes back falsy.

Response — success

{
  "data": {
    "metaPixelId": "1234567890",
    "googleAnalyticsId": "G-AB12CD34EF",
    "googleAdsConversionId": "AW-1234567890",
    "googleAdsPurchaseLabel": "abcDEF1234",
    "tiktokPixelId": "C4ABC123…",
    "enabled": true
  },
  "error": null,
  "meta": { "requestId": "...", "timestamp": "..." }
}

Response — nothing to fire

{ "data": null, "error": null, "meta": { ... } }

Note what's missing from the success response: metaCapiAccessToken and metaTestEventCode. These are server-side secrets and are never returned from /public/:accountId regardless of enabled state. If you see them leaking in any future release, file a security ticket immediately.

The merchant pixels object

Field Type Nullable Notes
id string no mxp_ + ULID.
accountId string no Unique — one row per merchant.
metaPixelId string yes Public, embed-safe.
metaCapiAccessToken string yes Secret. Server-side only.
metaTestEventCode string yes Optional; only set during CAPI integration testing.
googleAnalyticsId string yes Public.
googleAdsConversionId string yes Public.
googleAdsPurchaseLabel string yes Public.
tiktokPixelId string yes Public.
enabled boolean no Master switch.
createdAt, updatedAt ISO 8601 no

CAPI helper: hashEmail

The Node and Python SDKs export a hashEmail(email) helper that returns the SHA-256 hex digest of email.trim().toLowerCase() — the exact normalised form Meta's CAPI expects in its em field. Use it when constructing event payloads in your own code:

import { hashEmail } from '@forjio/ripllo-node';
const em = hashEmail('Alice@Example.com');
// → "5a13d2..." (hex)

Re-implementing this normalisation incorrectly is the most common reason CAPI events don't match. The helper is exported so you never have to.

Security review checklist

When changing this resource (route, schema, or the SDK surface), confirm:

  • The /public/:accountId select list explicitly lists only the public IDs — never spreads ...row.
  • metaCapiAccessToken and metaTestEventCode are not on the public select list.
  • Any new secret field added here is added to both the merchant-facing select (so it surfaces) and the public-facing select-exclusion list (so it doesn't leak).
  • The integration test in pixels.test.ts asserts that metaCapiAccessToken is undefined in the public response.

This resource is high-blast-radius: a leak of metaCapiAccessToken lets anyone with the storefront URL impersonate the merchant when posting conversions to Meta, which corrupts attribution and can get the merchant's Meta account flagged.

Events

The pixels resource doesn't emit outbox events. Pixel configuration changes are audited via the audit log under the operating user, but they don't fan out as webhooks — treating credential changes as broadcastable events would be its own leak.

Next

  • Feeds — the other side of ad-network plumbing (product catalog feeds rather than event tracking).
  • Channels — integrations the merchant connects for outbound marketing.