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:
| Capability | Amperity API | Direction | What it unlocks |
|---|---|---|---|
| Event ingest | Streaming Ingest (/prof/events/) | Signal → Amperity | Every Datafly event updates Amperity’s profile aggregations within seconds. |
| Profile enrichment | Profile API (/prof/profiles/) | Signal ← Amperity | Server-side lookup of visitor attributes (Persona, FavoriteBrand, LifetimeTier, …) attached to every event before delivery to downstream destinations. |
| Browser personalization | Profile API (via Signal) | Browser ← Signal | window.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.
acmeinhttps://acme.amperity.com). Used to build every API URL. - Tenant ID — the value Amperity expects in the
Amperity-TenantHTTP 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.
Configure Profile API access (optional but recommended)
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_keysarray determines which identifiers Signal can use to resolve visitors. Add at least one of:_dfdid(device cookie),email_sha256,phone_sha256, or a customer-specifiedaccount_id. Ask your Amperity admin to add these if they aren’t configured.
Configuration
| Field | Type | Required | Description |
|---|---|---|---|
tenant_subdomain | string | Yes | URL host component (e.g. acme for acme.amperity.com). |
tenant_id | string | Yes | Value sent as Amperity-Tenant header. Usually matches the subdomain. |
api_key | secret | Yes | Streaming Ingest JWT (write access). |
event_stream_id | string | Yes | Realtime stream identifier, prefixed esm-. |
source_value | string | Yes | Value 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_key | secret | No | Profile API JWT (read-only). Required for profile enrichment + personalization. |
profile_collection_id | string | No | Profile collection to query, prefixed apc-. Required when Profile API Key is set. |
linking_keys | textarea (JSON) | No | Array of {source, key, hash} specs driving how Signal resolves visitors against the Profile API. See Linking keys. |
browser_attribute_allowlist | string (CSV) | No | Comma-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:
| Preset | Event coverage |
|---|---|
| Retail | pageview, view_item_list, view_item, add_to_cart, view_cart, begin_checkout, purchase. Accepts both GA4 (view_item) and legacy Segment (Product Viewed) names. |
| Travel | flight_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 & Banking | account_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 / SaaS | signed_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. |
| Media | Video lifecycle: video_playback_started/paused/resumed/completed, video_content_started/completed/milestone, video_ad_started/completed/skipped. Audio + podcast: audio_playback_*, podcast_subscribed. |
| Gaming | game_started/ended, level_started/completed/failed, achievement_unlocked, tutorial_*, item_purchased/equipped, currency_earned/spent, friend_invited, match_started/completed, leaderboard_viewed. |
| Publishing | article_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_tierThis 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:
- Setting Enable Context-Sensitive Personalization on the integration.
- 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_personalizedand — if the browser allowlist is configured — flows through the same/v1/enrichpipe towindow.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
- Event-processor resolves the visitor and fetches attributes (as above).
- Result is filtered through
browser_attribute_allowlistand written to a separate Redis cache keyed by the visitor’s_dfdid. - Datafly.js fires
GET /v1/enrich?provider=amperityagainst identity-hub on init; the endpoint reads the filtered cache and returns it. - Browser code reads
datafly.profile.amperityafterdatafly.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.
Consent
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 fullcontext.consent.categoriesmap).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_dfdiddevice cookie),anonymous_id,user_id,email,phone.key— the name that matches an entry in your Amperity collection’slinking_keys. Amperity rejects lookups against keys not in that list.hash— optional.sha256applies 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 event | Amperity type field |
|---|---|
page | pageview |
view_item / Product Viewed | view_item |
view_item_list / Product List Viewed | view_item_list |
add_to_cart / Product Added | add_to_cart |
Product Removed | remove_from_cart |
view_cart / Cart Viewed | view_cart |
begin_checkout / Checkout Started | begin_checkout |
purchase / Order Completed | purchase |
Unmapped events pass through (this is a CDP) — Amperity’s routing expression decides what to ingest vs. route to _unknown.
Testing
Event ingest
- Fire a test event from your website or Signal’s event API.
- 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 status202. - In Amperity, navigate to your stream’s event inspector. Within ~5 seconds the event appears; within ~15 seconds the profile’s
updated_atand any configured aggregations update.
Profile enrichment
- Ensure Profile API creds + linking keys are set.
- Fire an event from a visitor whose identifier matches an existing profile (e.g. send an
identifywith an email that’s in your Amperity collection). - Inspect the next outbound event — its
Transformed Eventtab should showpartner_attrs.amperitypopulated.
Browser personalization
- Ensure
browser_attribute_allowlistis set and datafly.js is built withenrichconfig. - Open browser devtools → Network tab.
- Load the page. You should see
GET /v1/enrich?provider=amperityfire after init, returning{provider: "amperity", attributes: {...}}with only the allowlisted keys. - In the console,
window.datafly.profile.amperityshould match the response’sattributes.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
Events return 202 but don’t appear in Amperity | Wrong source_value — event silently routed to _unknown | Check your stream’s routing expression in Amperity and match exactly (case-sensitive). |
dial tcp: no such host on {tenant_subdomain}.amperity.com | tenant_subdomain is wrong — it’s the URL host, not the header value | Try the subdomain portion of your Amperity dashboard URL. |
401 / 403 on event POST | Streaming Ingest JWT expired or wrong scope | Regenerate under Settings > Security > API Keys with Streaming Ingest Write Access. |
403 on Profile API GET | Using the Streaming Ingest JWT instead of the Profile API JWT | They’re distinct tokens with distinct scopes — generate a new one with Profile Read Access. |
| Profile lookups always 404 | Collection’s linking_keys doesn’t include any key Signal is sending | Ask your Amperity admin to add email_sha256 / _dfdid / etc. to the collection’s linking_keys. |
partner_attrs.amperity never appears on events | Visitor not resolved OR Profile API creds missing OR no linking keys configured | Check the event-processor logs for partner resolve failed warnings. |
window.datafly.profile.amperity is empty | No 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 Found | IDENTITY_HUB_ORG_ID env var not set on identity-hub — endpoint is feature-gated | Set 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.