Getting started

Security & safeguards

How to integrate safely, and the guardrails we enforce on our side.

Read this before you ship. Enrichment moves customer identity and PII, so treat the keys and the data with care. Some of this is on you, some we enforce.

Your API key

  • Server-side only. The X-Api-Key can both write (/identify, /staff, /usage_events) and read PII (/customer). Never put it in frontend code, App Bridge scripts, mobile apps, or anything a browser can see. If you need to send events from the browser, use the write-only pixel token instead (Pixel), it can push events and nothing else.
  • One key per app. Each tracked app has its own key and its data is isolated. Don't share a key across apps.
  • Rotate on suspicion. If a key is exposed, rotate it in the console (Settings → Integrations → Shopify Partners) and update your backend env var. Rotation invalidates the old key immediately.
  • Keep it in a secret store, not in source control, not in logs. Redact it from error reporting.

Sending customer data safely

  • Send only what you need. Custom fields and contacts are stored as-is. Don't push secrets, tokens, or fields you wouldn't want a teammate to see in the console.
  • Mark sensitive fields private. A field marked private in the console is never returned by /customer and never leaves the dashboard.
  • You are the source of truth for PII. If a merchant asks to be forgotten, delete their contacts and custom-field values (see GDPR below), we won't have deleted them for you.

GDPR / Shopify compliance

Shopify requires apps that store customer-identifiable data to honour its privacy webhooks. Because enrichment can hold merchant staff names and emails:

  • customers/redact / shop/redact → delete that shop's contacts and any custom-field values that contain PII. Use /customer to see what's stored, then unset (send null) or delete via the console/API.
  • customers/data_request → you can read everything we hold on a shop with GET /customer.
  • Don't push staff or contact PII you don't have a lawful basis to store.

What we enforce for you

These guardrails run on our side so a mistake or a leaked key can't do unbounded damage:

  • Bounded schema. A single /identify call writes at most a capped number of fields, and an app can auto-create only a bounded number of new field definitions. Beyond that, extra fields are ignored (the response tells you how many were written).
  • Computed fields are read-only. Database-linked custom fields are never overwritten by a push, their query is the source of truth.
  • Type is fixed on first write. A field's type is inferred from its first value and enforced afterward, a wrong-shaped later push is rejected rather than silently corrupting the column.
  • Idempotent by key. /identify upserts on the shop; /staff dedupes on the Shopify user id; /usage_events dedupes on eventId. Safe retries, no duplicates.
  • Reconcile never touches manual data. A reconcile: true staff sync only removes app-sourced contacts, contacts a human added by hand are protected.
  • Rate limits. The ingestion endpoints are rate-limited per app; sustained abuse returns 429. Back off and retry.
  • Workspace isolation. A key only ever reads or writes its own app's data, there is no cross-app or cross-workspace access.

Checklist before launch

  1. Key lives in a server-side secret, never shipped to a client.
  2. Browser events (if any) use the pixel token, not the API key.
  3. Sensitive custom fields are marked private.
  4. Your customers/redact handler deletes contacts + PII custom fields for the shop.
  5. You handle 429 with backoff.

On this page