Amperity

Enterprise customer data platform using AI-driven identity resolution to unify customer records and power personalised experiences.

Datafly Signal talks to Amperity over three separate API surfaces, each driving a different capability:

CapabilityAmperity APIDirectionWhat it unlocks
Event ingestStreaming Ingest (/prof/events/)Signal → AmperityEvery Datafly event updates Amperity’s profile aggregations within seconds.
Profile enrichmentProfile API (/prof/profiles/)Signal ← AmperityServer-side lookup of visitor attributes (Persona, FavoriteBrand, LifetimeTier, …) attached to every event before delivery to downstream destinations.
Browser personalizationProfile API (via Signal)Browser ← Signalwindow.datafly.profile.amperity hydrated on page load for client-side personalization. The Profile API token never touches the browser.

Prerequisites

Complete these steps in Amperity before configuring Signal.

Access your Amperity tenant

Log in to the Amperity dashboard at app.amperity.com. Two values you’ll need:

  • Tenant subdomain — the host in your dashboard URL (e.g. acme in https://acme.amperity.com). Used to build every API URL.
  • Tenant ID — the value Amperity expects in the Amperity-Tenant HTTP header. Usually the same as the subdomain, but in some multi-tenant demo/sandbox setups they differ.

Create a realtime event stream

Navigate to Sources > Streams and click Add Stream. Configure the routing expression to match the source value Datafly will send (e.g. routing on source == "DATAFLY" → your target event type).

After creation, copy the Event Stream ID — it’s prefixed esm- (e.g. esm-2VP6va3Zn).

⚠️

There is also a batch ingest path with IDs prefixed is-. Do not use this ID in Signal — it accepts events but does not drive realtime profile aggregation updates. Signal only supports the realtime esm- path.

Generate the Streaming Ingest API key

Go to Settings > Security > API Keys > Add API key. Select Streaming Ingest Write Access. Generate the JWT and copy the token immediately — it won’t be shown again.

For profile enrichment + browser personalization you also need:

  • Profile Collection ID — from the dashboard collection you want to query, prefixed apc- (e.g. apc-vPsQheAm).
  • Profile API Key — a separate JWT with Profile Read Access scope. Must be distinct from the Streaming Ingest key; Amperity rejects cross-scope usage with 403.
  • Linking keys — the collection’s linking_keys array determines which identifiers Signal can use to resolve visitors. Add at least one of: _dfdid (device cookie), email_sha256, phone_sha256, or a customer-specified account_id. Ask your Amperity admin to add these if they aren’t configured.

Configuration

FieldTypeRequiredDescription
tenant_subdomainstringYesURL host component (e.g. acme for acme.amperity.com).
tenant_idstringYesValue sent as Amperity-Tenant header. Usually matches the subdomain.
api_keysecretYesStreaming Ingest JWT (write access).
event_stream_idstringYesRealtime stream identifier, prefixed esm-.
source_valuestringYesValue of the top-level source field on every event. Case-sensitive — must exactly match your stream’s routing expression or events are accepted (202) but silently dropped.
profile_api_keysecretNoProfile API JWT (read-only). Required for profile enrichment + personalization.
profile_collection_idstringNoProfile collection to query, prefixed apc-. Required when Profile API Key is set.
linking_keystextarea (JSON)NoArray of {source, key, hash} specs driving how Signal resolves visitors against the Profile API. See Linking keys.
browser_attribute_allowliststring (CSV)NoComma-separated attribute names safe to expose to the browser. See Browser personalization.

Signal setup

Install the integration

Go to Integrations → Integration Library, find Amperity, click Install, and fill in all required fields with values from the prerequisites above.

Set linking keys (optional — for profile enrichment)

If you want Amperity profile attributes attached to every event (or exposed to the browser), configure the linking keys. See Linking keys below.

Set the browser allowlist (optional — for personalization)

To expose profile attributes to window.datafly.profile.amperity in datafly.js, list them as a comma-separated string. Anything not in this list is stripped server-side before it reaches the browser.

Attach to a pipeline

From the pipeline page, add the Amperity integration. The blueprint (v1.8.0) provides 40+ flat global mappings shared across every vertical, plus vertical-specific event handlers so events flow through with minimal customisation.

Pick a vertical preset

The blueprint ships seven vertical presets — pick the one that matches your business and the event handlers will be pre-configured for that domain:

PresetEvent coverage
Retailpageview, view_item_list, view_item, add_to_cart, view_cart, begin_checkout, purchase. Accepts both GA4 (view_item) and legacy Segment (Product Viewed) names.
Travelflight_searched, hotel_searched, car_searched, flight_viewed, hotel_viewed, booking_started/completed/cancelled/modified, check_in_completed, check_out_completed, loyalty_points_earned/redeemed, review_submitted.
Finance & Bankingaccount_opened/closed, kyc_started/completed/failed, login_succeeded/failed, transaction_initiated/completed/failed, payment_made, transfer_completed, card_applied/approved/activated, loan_applied/approved/funded, statement_viewed.
B2B / SaaSsigned_up, logged_in/out, password_reset_requested, trial_started/converted/expired, subscription_started/upgraded/downgraded/cancelled/renewed, feature_used, invitation_sent/accepted, seat_added/removed, integration_connected.
MediaVideo lifecycle: video_playback_started/paused/resumed/completed, video_content_started/completed/milestone, video_ad_started/completed/skipped. Audio + podcast: audio_playback_*, podcast_subscribed.
Gaminggame_started/ended, level_started/completed/failed, achievement_unlocked, tutorial_*, item_purchased/equipped, currency_earned/spent, friend_invited, match_started/completed, leaderboard_viewed.
Publishingarticle_viewed/read/shared/bookmarked, category_viewed, search_performed, newsletter_subscribed/unsubscribed, paywall_viewed, subscription_started/cancelled, comment_posted, author_followed.

All presets share the same global mappings (identity, network, locale, page, session, campaign, geo, vendor IDs, consent), so the only difference between them is the event handlers and the property names they pull off event.properties.*. Unmapped events still pass through (the blueprint defaults to pass_through), so you can ship custom event names without changing the blueprint — the pipeline transformation engine simply forwards them flat.

Event ingest (Streaming Ingest)

Every event Signal processes is forwarded to the realtime /prof/events/{event_stream_id} endpoint:

POST https://{tenant_subdomain}.amperity.com/prof/events/{event_stream_id}
Amperity-Tenant: {tenant_id}
Authorization: Bearer {api_key}
Content-Type: application/json
 
{
  "source": "{source_value}",
  "type": "view_item_list",
  "cookieid": "dfid_abc123",
  "ip_address": "...",
  "user_agent": "...",
  "timestamp": "2026-04-17T15:37:10.484Z",
  "email": "...",
  "amperity_id": "...",
  ...30+ flat attributes from the blueprint
}

Datafly sends flat payloads by default — Amperity accepts nested objects too, but flat means customers don’t need to write flatten scripts on the Amperity side. See the blueprint source for the full mapping.

Server-side profile enrichment

When profile_api_key + profile_collection_id are set, the event-processor’s partner enrichment step runs on every event:

Resolve the visitor

Using the configured linking keys, Signal issues Profile API lookups until one matches. Result cached in Redis (15 min positive, 5 min negative) so subsequent events skip the call.

Fetch profile attributes

Given the resolved amperity_id, Signal calls GET /prof/profiles/{collection_id}/{amperity_id} and receives the full attribute map.

Attach to the event

Attributes land on event.Context.PartnerAttrs.amperity and are accessible to every downstream blueprint via source paths like:

global:
  mappings:
    - source: partner_attrs.amperity.Persona
      target: user_persona
    - source: partner_attrs.amperity.LifetimeTier
      target: user_tier

This lets Meta CAPI, GA4, TikTok, etc. receive enriched Amperity attributes on every event without each integration doing its own Amperity lookup.

Context-sensitive personalization

Amperity can return different attribute values depending on the visitor’s current context (which product category they’re on, what’s in their cart, etc.). Enable it by:

  1. Setting Enable Context-Sensitive Personalization on the integration.
  2. Configuring Personalization Context Mappings — a JSON array of {source, target} pairs describing what context values Signal forwards to Amperity on each call.
[
  {"source": "properties.category", "target": "pageCategory"},
  {"source": "properties.value",    "target": "cartValue"},
  {"source": "context.page.path",   "target": "pagePath"},
  {"source": "partner_attrs.amperity.LifetimeTier", "target": "tierHint"}
]

How it works

  • Server-side only. The browser never passes context values — they’re computed from the current event using Signal’s standard path syntax (context.*, properties.*, partner_attrs.*, flat event fields).
  • Amperity is configured to interpret the context keys you send (e.g. configure attribute logic in Amperity that uses pageCategory).
  • Result merges into event.Context.PartnerAttrs.amperity_personalized and — if the browser allowlist is configured — flows through the same /v1/enrich pipe to window.datafly.profile.amperity.
  • Cache key includes a hash of the context values, so different page/cart states produce independent cache entries (2-minute TTL). Same context → same cached response → no extra Profile API call.

Use personalization sparingly — every unique context hash is a cache miss and a Profile API call. Good targets: small-cardinality values like page category or LTV tier. Avoid URL parameters, timestamps, or other high-entropy fields.

Browser personalization

Datafly.js auto-hydrates window.datafly.profile.amperity with the allowlisted attribute subset on page load — no Profile API token touches the browser, no extra HTTP calls from the page.

Flow

  1. Event-processor resolves the visitor and fetches attributes (as above).
  2. Result is filtered through browser_attribute_allowlist and written to a separate Redis cache keyed by the visitor’s _dfdid.
  3. Datafly.js fires GET /v1/enrich?provider=amperity against identity-hub on init; the endpoint reads the filtered cache and returns it.
  4. Browser code reads datafly.profile.amperity after datafly.onProfileReady() resolves.

Page-side usage

datafly.init({
  pipelineKey: '...',
  endpoint: 'https://collect.example.com',
  // Baked in by the JS Builder when Amperity personalization is enabled:
  enrich: {
    endpoint: 'https://id.example.com',
    providers: ['amperity'],
  },
});
 
await datafly.onProfileReady();
const persona = datafly.profile.amperity?.Persona;
if (persona === 'Value Hunter') {
  document.querySelector('#hero').dataset.variant = 'promo';
}
 
// After a user logs in / identifies, you can refresh to pick up richer attributes:
datafly.identify('user-123', { email: '[email protected]' });
await datafly.refreshProfile();

What the browser can and cannot see

  • ✅ Only attributes in browser_attribute_allowlist — configured server-side, not overridable by page code.
  • ✅ Empty-attributes response (HTTP 200 with attributes: {}) for first-touch visitors with no resolved profile.
  • ❌ Never the Profile API token.
  • ❌ Never the raw Amperity response — always filtered server-side first.

Every event carries the visitor’s full consent state to Amperity in a nested consent field. This is unconditional — Datafly always forwards consent, there is no option to strip it. Amperity uses it to suppress or downgrade processing per visitor in line with the permission they granted.

Shape sent on every event:

{
  "consent": {
    "analytics": true,
    "marketing": false,
    "functional": true,
    "categories": {
      "C0001": true,
      "C0002": false,
      "ad_personalization": false
    },
    "google": {
      "ad_storage": "denied",
      "ad_user_data": "denied",
      "ad_personalization": "denied",
      "analytics_storage": "granted"
    }
  }
}

Why it’s nested, not flat

Every CMP has a different taxonomy — OneTrust uses C0001..C0004, CookieYes uses analytics/marketing/functional, TrustArc uses custom codes, plus Google Consent Mode v2 has its own four-parameter shape. Flattening to specific field names would lock the blueprint to one CMP vendor. Nesting preserves the full consent context verbatim, and Amperity-side extractors pick out whatever categories the customer cares about.

What’s included

  • consent.analytics / marketing / functional — Datafly’s canonical booleans. Present when the CMP exposes these as top-level concepts.
  • consent.categories — arbitrary CMP category codes (the full context.consent.categories map).
  • consent.google — Google Consent Mode v2 values when available (ad_storage, analytics_storage, ad_user_data, ad_personalization).

Visitors with no CMP interaction have the field absent or partial — Amperity should treat absence as “no consent given” per the customer’s usual defaults.

Linking keys

Amperity collections maintain a linking_keys allowlist controlling which identifiers Datafly can pass to the Profile API. Configure Signal to match:

[
  {"source": "email", "key": "email_sha256", "hash": "sha256"},
  {"source": "canonical_id", "key": "_dfdid"},
  {"source": "user_id", "key": "customer_id"}
]

Each entry:

  • source — where Signal pulls the value from on the Datafly side. One of: canonical_id (the _dfdid device cookie), anonymous_id, user_id, email, phone.
  • key — the name that matches an entry in your Amperity collection’s linking_keys. Amperity rejects lookups against keys not in that list.
  • hash — optional. sha256 applies lowercased-trimmed SHA-256 to the source value before sending. Use this for email/phone when your Amperity collection uses hashed PII linking keys.

Resolution strategy: Signal tries each entry in order and returns the first match. If a source field is empty on a visitor (e.g. anonymous user with no email), that entry is skipped.

For the ACME2 sales-demo collection linking_keys: [] — lookups always 404. Production customers typically configure at least email_sha256 and _dfdid.

Event name mappings

The v1.8.0 blueprint’s Retail preset handles both GA4-style and Segment-style event names that datafly.js might emit. Other vertical presets follow GA4-style names from the corresponding event spec:

Datafly.js eventAmperity type field
pagepageview
view_item / Product Viewedview_item
view_item_list / Product List Viewedview_item_list
add_to_cart / Product Addedadd_to_cart
Product Removedremove_from_cart
view_cart / Cart Viewedview_cart
begin_checkout / Checkout Startedbegin_checkout
purchase / Order Completedpurchase

Unmapped events pass through (this is a CDP) — Amperity’s routing expression decides what to ingest vs. route to _unknown.

Testing

Event ingest

  1. Fire a test event from your website or Signal’s event API.
  2. In Signal, open Live Events and click the event — the Vendor Request tab should show POST https://{tenant_subdomain}.amperity.com/prof/events/{event_stream_id} with status 202.
  3. In Amperity, navigate to your stream’s event inspector. Within ~5 seconds the event appears; within ~15 seconds the profile’s updated_at and any configured aggregations update.

Profile enrichment

  1. Ensure Profile API creds + linking keys are set.
  2. Fire an event from a visitor whose identifier matches an existing profile (e.g. send an identify with an email that’s in your Amperity collection).
  3. Inspect the next outbound event — its Transformed Event tab should show partner_attrs.amperity populated.

Browser personalization

  1. Ensure browser_attribute_allowlist is set and datafly.js is built with enrich config.
  2. Open browser devtools → Network tab.
  3. Load the page. You should see GET /v1/enrich?provider=amperity fire after init, returning {provider: "amperity", attributes: {...}} with only the allowlisted keys.
  4. In the console, window.datafly.profile.amperity should match the response’s attributes.

Troubleshooting

SymptomLikely causeFix
Events return 202 but don’t appear in AmperityWrong source_value — event silently routed to _unknownCheck your stream’s routing expression in Amperity and match exactly (case-sensitive).
dial tcp: no such host on {tenant_subdomain}.amperity.comtenant_subdomain is wrong — it’s the URL host, not the header valueTry the subdomain portion of your Amperity dashboard URL.
401 / 403 on event POSTStreaming Ingest JWT expired or wrong scopeRegenerate under Settings > Security > API Keys with Streaming Ingest Write Access.
403 on Profile API GETUsing the Streaming Ingest JWT instead of the Profile API JWTThey’re distinct tokens with distinct scopes — generate a new one with Profile Read Access.
Profile lookups always 404Collection’s linking_keys doesn’t include any key Signal is sendingAsk your Amperity admin to add email_sha256 / _dfdid / etc. to the collection’s linking_keys.
partner_attrs.amperity never appears on eventsVisitor not resolved OR Profile API creds missing OR no linking keys configuredCheck the event-processor logs for partner resolve failed warnings.
window.datafly.profile.amperity is emptyNo browser_attribute_allowlist configured (empty = nothing exposed)Add attribute names as CSV. Anything not in the list is stripped server-side before reaching the browser.
/v1/enrich returns 404 Not FoundIDENTITY_HUB_ORG_ID env var not set on identity-hub — endpoint is feature-gatedSet the env var to the org UUID on the identity-hub Coolify resource and redeploy.

Visit Amperity documentation for full API reference and Streaming Ingest configuration details.