IdentityServer-Proxied Enrichment

Server-Proxied Enrichment

Some identity vendors require API credentials (API keys, tokens, or secrets) to resolve an identity. These credentials cannot be exposed to the browser. Signal handles this through server-proxied enrichment: the browser initiates the request, the Ingestion Gateway adds credentials server-side, calls the vendor API, stores the result in Redis, and returns the ID to the browser for local caching.

Why Server Proxying Is Needed

Self-generated IDs and click ID capture cover the majority of vendor identity needs. However, some identity vendors — particularly data onboarders and identity graph providers — require an authenticated API call to resolve an identity:

  • The API requires an API key, secret, or bearer token
  • The API may require hashed PII (email, phone) that must be sent from a trusted server
  • The API response contains sensitive identity tokens that should be stored securely

In all these cases, the browser cannot call the API directly because doing so would expose credentials in client-side code. Signal solves this by proxying the request through the Ingestion Gateway.

Enrichment Flow

1. Browser (Datafly.js)
   → Sends enrichment request to Ingestion Gateway
     POST /v1/enrich
     { "vendor": "liveramp_ats", "anonymous_id": "f47ac10b..." }

2. Ingestion Gateway
   → Reads vendor credentials from encrypted config (PostgreSQL)
   → Adds credentials to the request
   → Calls vendor API server-side:
     POST https://api.rlcdn.com/api/identity/v2/envelope
     Authorization: Bearer {api_key}
     { "identifiers": [...] }

3. Vendor API responds
   → { "envelope": "XY1000bGluZWRpbjANCm..." }

4. Ingestion Gateway
   → Stores result in Redis immediately:
     HSET identity:{anonymous_id} ramp_id "XY1000bGluZWRpbjANCm..."
   → Returns ID to browser:
     { "vendor": "liveramp_ats", "id": "XY1000bGluZWRpbjANCm..." }

5. Browser
   → Stores ID in IndexedDB for local caching
   → ID is available in Redis for the next event (no round-trip delay)

Dual-Storage Advantage

The server-proxied enrichment pattern stores the identity result in two places simultaneously:

StorageLocationPurpose
RedisServer-side (identity:{anonymous_id})Immediately available for event processing
IndexedDBBrowser-side (Datafly.js)Avoids redundant enrichment calls on subsequent page loads

This dual-storage approach eliminates the “one-event delay” problem that plagues other server-side tag managers. In a naive implementation, the enrichment result would only be available after the browser sends another event. With Signal’s approach, the Ingestion Gateway stores the result in Redis immediately after receiving it from the vendor — so even if the current page’s events have already been processed, the enrichment result is available for any events that arrive milliseconds later.

On subsequent page loads, Datafly.js checks IndexedDB before requesting enrichment. If a valid (non-expired) result exists locally, no enrichment call is made.

Supported Vendors

Acxiom RealID

PropertyValue
Endpointapi.us-east-1.a.realid.rapid.acxiom.io
AuthenticationAPI key (header)
InputHashed email or device IDs
OutputAcxiom Person ID + demographic attributes
Redis fieldacxiom_person_id
TTL30 days

Acxiom RealID resolves anonymous visitors to Acxiom’s identity graph, returning a persistent Person ID and optional demographic attributes. This enables deterministic audience matching across channels.

Amperity

PropertyValue
EndpointCustomer-specific (e.g. api.amperity.com/v1/resolve)
AuthenticationBearer token
InputAnonymous ID + any known PII
OutputAmperity unified ID
Redis fieldamperity_id
TTL30 days

Amperity provides a customer data platform with identity resolution. The enrichment call resolves a Signal anonymous ID to an Amperity unified customer ID, enabling event delivery to be matched against Amperity’s unified customer profiles.

LiveRamp ATS

PropertyValue
Endpointapi.rlcdn.com
AuthenticationAPI key (header)
InputHashed email (SHA-256)
OutputRampID envelope (encrypted)
Redis fieldramp_id
TTL24 hours (envelope expiry)

LiveRamp’s Authenticated Traffic Solution (ATS) resolves hashed email addresses to RampID envelopes. RampIDs enable programmatic advertising use cases and are accepted by demand-side platforms for audience targeting.

⚠️

LiveRamp envelopes are short-lived (24 hours). Signal automatically refreshes the envelope on subsequent visits. The Redis TTL for ramp_id is set to match the envelope expiry.

UID2

PropertyValue
EndpointUID2 operator endpoint (region-specific)
AuthenticationAPI key + client secret
InputHashed email or phone (SHA-256, normalised)
OutputUID2 token (encrypted, rotatable)
Redis fielduid2_token
TTLMatches token refresh interval

Unified ID 2.0 is an open-source identity framework for the advertising ecosystem. Signal calls the UID2 operator to generate a UID2 token from hashed PII, which can then be included in programmatic ad requests.

The Trade Desk

PropertyValue
Endpointmatch.adsrvr.org/track/rid
AuthenticationPartner ID + secret
InputHashed email (SHA-256) or device IDs
OutputTDID (The Trade Desk ID)
Redis fieldtdid
TTL30 days

The Trade Desk ID (TDID) enables conversion attribution and audience targeting on The Trade Desk’s demand-side platform.

Generic Enrichment Pattern

In addition to the built-in vendor integrations, Signal supports custom enrichment endpoints. This allows customers to add their own identity providers or internal identity services using the same server-proxied pattern.

Custom enrichment endpoints are configured per source:

{
  "enrichment": {
    "custom_providers": [
      {
        "name": "internal_cdp",
        "endpoint": "https://cdp.internal.example.com/v1/resolve",
        "method": "POST",
        "authentication": {
          "type": "bearer",
          "token_secret_ref": "secret:cdp_api_token"
        },
        "request_template": {
          "anonymous_id": "{{anonymous_id}}",
          "email_hash": "{{sha256_email}}"
        },
        "response_mapping": {
          "redis_field": "cdp_customer_id",
          "json_path": "$.customer_id"
        },
        "ttl": "30d",
        "timeout": "2s"
      }
    ]
  }
}

The gateway interpolates template variables ({{anonymous_id}}, {{sha256_email}}, etc.) from the event context, calls the endpoint with the configured authentication, extracts the result using the JSON path expression, and stores it in Redis under the specified field name.

Custom enrichment endpoints must respond within the configured timeout (default 2 seconds). If the endpoint is slow or unavailable, the enrichment is skipped for that request and retried on the next visit.

Credential Security

Vendor API credentials are stored encrypted in PostgreSQL using AES-256-GCM encryption. The encryption key is provided via environment variable and is never stored in the database.

Credential storage:
  PostgreSQL → encrypted blob (AES-256-GCM)
  Encryption key → DATAFLY_ENCRYPTION_KEY environment variable

Credential flow:
  Management UI → Management API → encrypt → PostgreSQL
  Ingestion Gateway → PostgreSQL → decrypt → vendor API call
  Browser → never sees credentials

Credentials are decrypted in memory at the Ingestion Gateway only when making a vendor API call. They are never included in event payloads, Kafka messages, logs, or browser responses.

⚠️

Never log or expose enrichment API credentials. Signal’s logging is configured to redact credential values automatically. If you add custom enrichment endpoints, ensure your endpoint URLs do not contain credentials as query parameters.

Enrichment Timing

Server-proxied enrichment is asynchronous relative to the initial page event. The typical sequence is:

1. Page loads → Datafly.js sends page event (immediate)
2. Datafly.js checks IndexedDB for cached enrichment results
3. If no cached result → Datafly.js sends enrichment request to gateway
4. Gateway proxies to vendor API → stores result in Redis → returns to browser
5. Browser stores result in IndexedDB for future visits

Steps 1 and 3 happen in parallel. The page event is not blocked by the enrichment call. This means:

  • The first event from a new visitor may not have enrichment data (it is still in-flight)
  • The enrichment result is stored in Redis as soon as the vendor responds
  • Subsequent events from the same session (or future sessions) include the enrichment data

For most use cases, this latency is acceptable because conversion events (purchases, sign-ups) typically happen later in the session, by which time the enrichment result is available.

Error Handling

If a vendor enrichment call fails, Signal handles it gracefully:

ScenarioBehaviour
Vendor API timeout (>2s)Enrichment skipped, retried on next visit
Vendor API returns error (4xx/5xx)Logged, skipped, retried on next visit
Invalid credentialsLogged as configuration error, not retried until credentials are updated
Vendor API rate limit (429)Backed off, retried after Retry-After interval
Network errorEnrichment skipped, retried on next visit

Failed enrichment calls never block event processing. The event is processed and delivered with whatever identity data is available. The enrichment result will be included in future events once the vendor API responds successfully.