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 (/api/indexes/)Signal ← AmperityServer-side lookup of visitor attributes (Persona, FavoriteBrand, LifetimeTier, …) attached to every event before delivery to downstream destinations.
Browser personalisationProfile API (via Signal)Browser ← Signalwindow.datafly.profile.amperity hydrated on page load for client-side personalisation. 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 personalisation you also need:

  • Profile API Index — a published Profile API index, identified by its ID (e.g. apc-vPsQheAm). Each index has a single lookup key — the field you query it by. Publish one index keyed on amperity_id for enrichment, and (optionally) further indexes keyed on the identifiers you resolve visitors by (e.g. a hashed email or a device cookie).
  • 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.
  • Hosting region — Azure-hosted tenants are reached at https://{tenant_subdomain}.amperity.com. AWS-hosted tenants are reached at https://app.amperity.com — set the Profile API Base URL field accordingly. Ask your Amperity admin which region your tenant is in.

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 + personalisation.
profile_collection_idstringNoProfile API index ID to query (e.g. apc-…). Required when Profile API Key is set.
profile_api_base_urlstringNoHost override. Blank = https://{tenant_subdomain}.amperity.com (Azure). Set https://app.amperity.com for AWS-hosted tenants.
linking_keystextarea (JSON)NoArray of {source, key, hash, index} 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 personalisation.

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 personalisation)

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.

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": "anon_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. The full Amperity field mapping is browsable in the Management UI under the Amperity integration’s blueprint editor.

Server-side profile enrichment

When profile_api_key + profile_collection_id are set, Signal performs partner enrichment on every event:

Resolve the visitor

Using the configured linking keys, Signal issues Profile API lookups until one matches. Each lookup is a GET /api/indexes/{index_id}/profiles/{lookup_value} against the index keyed on that identifier. Signal reads the visitor’s amperity_id from the resolved profile (the amperity_id attribute, falling back to the response id). Successful resolutions are cached server-side so subsequent events skip the call; failed resolutions are dampened to keep partner API traffic bounded.

Fetch profile attributes

Given the resolved amperity_id, Signal calls GET /api/indexes/{index_id}/profiles/{amperity_id} (against an index keyed on amperity_id) and receives the full attribute map. Every call carries the required headers: Authorization: Bearer {profile_api_key}, Amperity-Tenant: {tenant_id}, and api-version: 2025-07-31. The response shape is {"id": …, "index_id": …, "attributes": { … }}.

Attach to the event

Attributes are attached to the event as partner_attrs.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 personalisation

⚠️

Amperity’s single-profile Profile API endpoint (GET /api/indexes/{id}/profiles/{value}) takes no request parameters, so Signal cannot forward per-request context (page category, cart state) to shape the response today. With personalisation enabled, the Personalize flow currently returns the same attributes as standard enrichment and merges them under partner_attrs.amperity_personalized. The configuration below is retained for forward compatibility and will begin forwarding context if Amperity exposes a parameterised endpoint.

Enable it by:

  1. Setting Enable Context-Sensitive Personalisation on the integration.
  2. Configuring Personalisation Context Mappings — a JSON array of {source, target} pairs.
[
  {"source": "properties.category", "target": "pageCategory"},
  {"source": "properties.value",    "target": "cartValue"},
  {"source": "context.page.path",   "target": "pagePath"}
]

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).
  • Result merges into partner_attrs.amperity_personalized and — if the browser allowlist is configured — flows through to window.datafly.profile.amperity.
  • The Personalize cache key includes a hash of the context values (2-minute TTL), so the wiring is ready for context-varying responses the moment Amperity supports them.

Browser personalisation

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. The visitor’s attributes are resolved server-side via the Amperity Profile API and filtered through browser_attribute_allowlist.
  2. The filtered profile is made available to Datafly.js on init.
  3. 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 Collector tab when Amperity personalisation 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: 'alice@example.com' });
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.

Identity

Signal sends the following identifiers to Amperity on every event (subject to which linking_keys are configured on the Amperity side):

FieldSourceNotes
cookieidDatafly anonymous identifierStable per-browser identifier. Always present.
user_iddatafly.identify() user IDPresent after the visitor logs in or is identified.
emailtraits.emailPlain text by default; can be hashed when matching against email_sha256 linking keys.
phonetraits.phonePlain text by default; can be hashed when matching against phone_sha256 linking keys.
ip_addressrequest IPForwarded from the original browser request.
user_agentrequest user-agentForwarded from the original browser request.

Call datafly.identify() when a user logs in or submits a form to maximise the identifiers available for resolution:

datafly.identify("user-123", {
  email: "jane.doe@example.com",
  phone: "+44 7700 900123"
});

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

Each Amperity Profile API index has a single lookup key. To resolve visitors by more than one identifier, publish one index per identifier and point each linking key at its index:

[
  {"source": "email", "key": "email_sha256", "hash": "sha256", "index": "apc-email"},
  {"source": "canonical_id", "key": "device_cookie", "index": "apc-cookie"},
  {"source": "user_id", "key": "customer_id"}
]

Each entry:

  • source — where Signal pulls the value from on the Datafly side. One of: canonical_id (the device cookie / anonymous identifier), anonymous_id, user_id, email, phone.
  • key — informational label for the identifier on the Amperity side (e.g. email_sha256). Not sent on the wire; the target index’s own lookup key determines matching.
  • hash — optional. sha256 applies lowercased-trimmed SHA-256 to the source value before sending. Use this for email/phone when the index is keyed on hashed PII.
  • index — optional Profile API index ID to query for this key. Defaults to the Profile API Index ID configured on the integration.

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.

If an index isn’t published, or the lookup value doesn’t match its key, the Profile API returns 404 and Signal moves to the next linking key. For the resolved amperity_id to be usable downstream, the resolution index should expose amperity_id as an attribute.

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.

Verify it’s working

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 personalisation

  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 valueUse 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.
400 / version error on Profile API GETWrong host for the tenant’s regionAWS tenants must set Profile API Base URL to https://app.amperity.com; Azure tenants use the subdomain host.
Profile lookups always 404The index ID isn’t a published index, or the lookup value doesn’t match the index’s keyConfirm the Profile API Index ID is published and keyed on the identifier you’re resolving by (per-linking-key index override if they differ).
partner_attrs.amperity never appears on eventsVisitor not resolved OR Profile API creds missing OR no linking keys configuredCheck Signal’s Live Events 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.

See also

  • Zeotap — alternative CDP with identity resolution
  • LiveRamp — RampID-based identity resolution
  • Event specs — canonical Datafly event schema

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