Skip to main content
A Direct API deployment lets you integrate Permutive by making API calls directly, rather than deploying one of our SDKs. This approach is suited to environments where deploying an SDK is not feasible — for example, certain CTV or hybrid TV setups with technical constraints, non-owned-and-operated (non-O&O) properties where you don’t control the application environment, or custom server-side architectures.
If your environment supports one of our SDKs, that is the recommended approach — the SDK handles identity, event collection, caching, and activation delivery automatically. Use a Direct API deployment only when an SDK is not feasible.

Deployment Flow

A Direct API deployment involves two Permutive APIs, each serving a different purpose:
APIPurposeRequires consentRequires user identity
Custom Cohort Segmentation (CCS) APICollect events and retrieve behavioral cohort signalsYesYes
Contextual APIRetrieve contextual cohort signals based on contentNoNo
Your application orchestrates these APIs, merges their results, and passes the combined signals to your ad server for activation. The sections below walk through each step in order. Your application is responsible for enforcing consent before collecting any user data.
  • CCS API — only call this for users who have given appropriate consent under your applicable privacy framework (GDPR, CCPA, etc.). The API processes all events it receives; there is no server-side consent gate.
  • Contextual API — this can be called for all users, regardless of consent status, because it evaluates content properties rather than user behavior.
Do not send events to the CCS API for users who have not consented. Unlike the SDK, which can be configured with consentRequired to gate data collection, the API has no built-in consent check.
This means every user session follows one of two paths:
User consent statusAPIs to callSignals available
ConsentedCCS API + Contextual APIBehavioral + Contextual
Not consentedContextual API onlyContextual only

Step 2: Manage Identity

For consented users, you need a stable identifier to send with CCS API requests. Your application owns the identity lifecycle — generating, persisting, and passing identifiers consistently.

Choosing a User Identifier

Permutive supports three types of user identifier. They differ in scope — that is, how broadly the identifier can link a user across different contexts. We recommend using the broadest-scope identifier available in your environment.
#User identifierScopeLinks a user across…
1Authenticated first-party ID (e.g., hashed email, internal login ID)BroadestMultiple devices and environments
2Device ID (e.g., IDFA, AAID, CTV device identifiers, Alexa Advertising ID)Device-levelMultiple applications on the same device
3Generated Permutive ID (a Version 4 UUID your application generates)Application-levelSessions within the current application only
A Direct API deployment does not require third-party identifiers such as cookies, universal IDs, or authenticated logins. A device ID or a self-generated Permutive ID is sufficient for real-time, cross-session behavioral segmentation. This means your deployment is not constrained by identity availability — even in environments where traditional identifiers are limited (e.g., iOS, CTV).
If more than one identifier is available, you can pass them all in a single request — Permutive will use the highest-priority identifier and retain the others for identity resolution.

Sending the User Identifier

Authenticated First-Party ID

Pass as an alias, with the tag matching a user identifier configured in your Permutive workspace (e.g., email_sha256, internal_id):
{
  "alias": {
    "tag": "email_sha256",
    "id": "a1b2c3d4e5f6..."
  },
  "events": [...]
}

Device ID

Pass as an alias, with the tag matching the device identifier type (e.g., idfa, aaid, tifa, alexa_advertising_id):
{
  "alias": {
    "tag": "idfa",
    "id": "6D92078A-8246-4BA4-AE5B-76104861E7DC"
  },
  "events": [...]
}

Generated Permutive ID

For users without an authenticated ID or accessible device ID, your application can generate a Version 4 UUID on-device, persist it in local storage, and reuse it across sessions. Pass it directly as user_id:
{
  "user_id": "2008c38f-dece-4570-976d-87593ed001c3",
  "events": [...]
}

Multiple Identifiers

If you have more than one identifier available, pass them in aliases with priorities (lower number = higher priority). Ordering by scope maximizes the chance of linking the user across sessions, devices, and environments:
{
  "aliases": [
    { "tag": "email_sha256", "id": "a1b2c3d4e5f6...", "priority": 0 },
    { "tag": "idfa", "id": "6D92078A-8246-4BA4-AE5B-76104861E7DC", "priority": 1 }
  ],
  "events": [...]
}

Key Considerations

  • Consistency — always use the same identifier for the same user. Inconsistent identifiers create duplicate profiles and fragment cohort history.
  • Uniqueness - make sure the identifiers are unique for each individual user. If multiple users share the same identity then all behavior from those users will be shared between them.
  • Persistence — a generated Permutive ID must be stored in durable storage (e.g., device local storage) so it survives across sessions.
  • Workspace configuration — user identifiers must be configured in your Permutive workspace before use. You can do this in the Permutive dashboard — see Configuring identifiers.
  • Identity resolution — when multiple identifiers map to different Permutive user IDs, the highest-priority identifier (lowest number) wins and all future activity resolves to that user ID. Historical state under other user IDs is not retroactively merged into the winning profile — so consistent priority ordering across all platforms is important from the start. See Identity concepts and the Identify API reference for the full resolution model.

Step 3: Collect Data via the CCS API

For consented users, send events to the CCS API to collect behavioral data and trigger real-time cohort evaluation. Each request contains a user identity and one or more events.

Event Structure

Each event requires a name, time, and properties object that matches the schema defined in your Permutive workspace.
FieldTypeRequiredDescription
namestringYesThe event name as defined in your workspace (e.g., "Videoview", "AudioPlay")
timestringYesISO 8601 timestamp of when the event occurred
view_idUUIDNoGroups events within a single content engagement (e.g., watching a TV program or listening to a podcast)
session_idUUIDNoGroups events within a single session, across multiple content engagements
propertiesobjectYesEvent properties matching your workspace schema
Each request can include up to 10 events. If you need to send more, split them across multiple requests. Events should be sent in chronological order.
If a property is not applicable or not available for a given environment (e.g., bluetooth_audio_active on a web client), omit it entirely rather than sending it as null or an empty value.

Session and View IDs

  • session_id — generate a new UUID at the start of each user session and reuse it for all events within that session, which may span multiple content engagements. Define what a “session” means for your application (e.g., a 30-minute inactivity timeout).
  • view_id — generate a new UUID for each content engagement (e.g., a video watched or a podcast listened to), and include it on every event that occurs within that engagement.
These IDs are required for creating cohorts which target using ‘this session’ and ‘the current page’ time windows.

Play and Completion Events

For any given content engagement, we recommend tracking two events:
  • A play event when playback starts (e.g., Videoview, AudioPlay) — captures content metadata
  • A completion event when playback ends or the user stops (e.g., VideoCompletion, AudioCompletion) — captures engagement metrics such as time spent and percentage completed
Both events share the same view_id to link them together. The exact event names and property schemas are configured in your Permutive workspace during onboarding.
1. Play event — sent when the video starts playing:
{
  "name": "Videoview",
  "time": "2024-01-15T14:00:00Z",
  "view_id": "a2b3c4d5-e6f7-8901-2345-6789abcdef01",
  "session_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "properties": {
    "video_id": "vid_456",
    "title": "Highlights: Premier League Week 20",
    "categories": ["sports", "football"],
    "duration_seconds": 600
  }
}
2. Completion event — sent when the video ends or the user stops watching:
{
  "name": "VideoCompletion",
  "time": "2024-01-15T14:07:30Z",
  "view_id": "a2b3c4d5-e6f7-8901-2345-6789abcdef01",
  "session_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "properties": {
    "video_id": "vid_456",
    "title": "Highlights: Premier League Week 20",
    "categories": ["sports", "football"],
    "duration_seconds": 600,
    "time_spent_seconds": 450,
    "percentage_watched": 0.75
  }
}
Cohort rules often depend on engagement data from the completion event. If you skip it, cohorts that use conditions like “listened for more than 30 seconds” or “watched at least 50%” will not evaluate correctly.

Engagement Metrics

Your application is responsible for measuring engagement and including it on the completion event. Two metrics are typically captured:
MetricDescription
Time spentTotal seconds the user spent engaging with the content
CompletionProportion of the content consumed (0–1). For video and audio, this is the percentage watched or listened to. For article web pages, it is the scroll depth.

Step 4: Retrieve Behavioral Cohort Signals

The CCS API response contains the user’s updated cohort membership and, if requested, activations grouped by ad platform.

Response Format

{
  "user_id": "2008c38f-dece-4570-976d-87593ed001c3",
  "cohorts": ["12345", "67890", "11111"],
  "activations": {
    "freewheel": ["12345", "67890"],
    "adswizz_keyvalue": ["12345"],
    "dfp": ["12345", "67890", "11111"]
  }
}
  • user_id — the Permutive user ID assigned to this user.
  • cohorts — all cohort IDs the user currently belongs to.
  • activations — cohorts grouped by the ad platform they are activated for. Only included if activations=true is set as a query parameter. The keys (e.g., freewheel, adswizz_keyvalue, dfp) correspond to activation platforms configured in your Permutive dashboard.
Always request activations by adding ?activations=true to your API call if you intend to use cohorts for ad targeting.
When attaching cohorts to an ad request, use the per-platform activations object — not the top-level cohorts array. The cohorts array includes every cohort the user belongs to, many of which may not be relevant to (or activated for) the specific ad platform you are calling. Using the per-platform activations keeps your ad request focused on cohorts that have been explicitly activated for that platform.

Caching

Call the CCS API on every content view to keep cohort membership up to date. However, cache the most recent response as a fallback:
  1. Cache each response — store the latest cohorts and activations in a location accessible to your ad-serving logic (server-side session, in-memory cache, or device storage).
  2. Use cached values as fallback — if the API call fails or is too slow, use cached cohorts from the previous response.
  3. Set a TTL — a long TTL (e.g., 30 days or longer) is reasonable since the cached data is a short list of cohort IDs.
Do not skip API calls and rely solely on cached data. Cohort membership updates in real time based on user behavior, and stale data reduces targeting accuracy.

Step 5: Retrieve Contextual Cohort Signals

The Contextual API returns cohort signals based on the content being viewed, rather than the user’s behavior. This means it:
  • Does not require user identity
  • Does not require consent
  • Should be called for every content view, regardless of consent status
The Contextual API takes a content URL and the same content properties you send in your CCS API events — minus any user-related data — and returns contextual cohort membership.

Example Request

curl -X POST "https://api.permutive.com/ctx/v1/segment?k=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "<string>",
    "page_properties": {
      "client": {
        // Client properties
      },
      "context": {
        // Context properties
      },
      "audio": {
        // Audio properties (or video, article, etc. — matching your workspace schema)
      }
    }
  }'
Mirror the properties object from your CCS API event, omitting any user-related fields. This ensures the Contextual API classifies the same content the user is engaging with. See the Contextual API reference for full request and response details.

The url Field

The Contextual API requires a url field. Any URL scheme is accepted, so in audio, video, or other non-web environments, construct a URL that uniquely and deterministically identifies the active content. Recommendations:
  • Use the public web URL where one exists (e.g., https://example.com/shows/morning-news/). If the content has a web presence that drives traffic, Permutive may already have URL-based affinity scores associated with it, improving contextual classification.
  • Use an app-schema URL for in-app content (e.g., myapp://podcast/1234) when no public web URL applies.
  • Construct a deterministic URL for environments without a natural URL (e.g., smart speakers). Build one from a stable content identifier, such as smartspeaker://station/abc123.
The same piece of content should always resolve to the same URL — consistency is what allows Permutive to build and match contextual signals reliably.
Contextual cohorts are configured separately in the Permutive dashboard. If your workspace uses contextual cohorts, this API call is essential for maximizing signal coverage — especially for unconsented users where it is the only source of cohort signals.

Step 6: Merge Signals and Activate

Before passing cohort signals to your ad server, merge the behavioral activations (from the CCS API) with the contextual activations (from the Contextual API) into a single set of key-values.
Behavioral activations (CCS API):        {"freewheel": ["12345", "67890"]}
Contextual activations (Contextual API): {"freewheel": ["99999", "88888"]}

Merged activations:                      {"freewheel": ["12345", "67890", "99999", "88888"]}
For unconsented users, you will only have contextual activations — pass those directly.
In some cases it may make sense to send behavioral and contextual cohorts as separate key-values (e.g., permutive and prmtvctx) rather than merging them into one. This can enable separate reporting and targeting on each signal type in your ad platform. Discuss with your Customer Success Manager to decide on the right approach for your ad stack.

Attaching Signals to Ad Requests

Once merged (or split, depending on your approach), append the signals to your ad request as key-values. The exact format depends on your ad platform — we recommend working with Technical Services during setup to confirm the correct key-value structure for each platform you are activating against.

End-to-End Example

The following sequence diagram shows a complete content engagement — from playback start through to completion — illustrating how the CCS API, Contextual API, and ad server interact in practice.

Request and Response Payloads

Expand each step below to see the corresponding request or response body. Step numbers match the sequence diagram above.
The play event sent when playback starts, including the user identifier and content metadata:
{
  "aliases": [
    { "tag": "tifa", "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "priority": 0 }
  ],
  "events": [{
    "name": "Videoview",
    "time": "2024-01-15T14:00:00Z",
    "view_id": "a2b3c4d5-e6f7-8901-2345-6789abcdef01",
    "session_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "properties": {
      "video_id": "vid_456",
      "title": "Highlights: Premier League Week 20",
      "categories": ["sports", "football"],
      "duration_seconds": 600
    }
  }]
}
The response includes the user’s cohort membership and activations grouped by ad platform:
{
  "user_id": "2008c38f-dece-4570-976d-87593ed001c3",
  "cohorts": ["12345", "67890", "11111"],
  "activations": {
    "freewheel": ["12345", "67890"],
    "dfp": ["12345", "67890", "11111"]
  }
}
Mirrors the content properties from the CCS API event, with a URL and no user-related data:
{
  "url": "https://example.com/shows/pl-highlights/",
  "page_properties": {
    "video": {
      "video_id": "vid_456",
      "title": "Highlights: Premier League Week 20",
      "categories": ["sports", "football"],
      "duration_seconds": 600
    }
  }
}
Contextual cohort activations based on the content, independent of user identity:
{
  "activations": {
    "freewheel": ["99999"],
    "dfp": ["88888"]
  }
}
The completion event sent when playback ends. Note the same user identifier, view_id, and session_id as the play event, with added engagement metrics:
{
  "aliases": [
    { "tag": "tifa", "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "priority": 0 }
  ],
  "events": [{
    "name": "VideoCompletion",
    "time": "2024-01-15T14:07:30Z",
    "view_id": "a2b3c4d5-e6f7-8901-2345-6789abcdef01",
    "session_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "properties": {
      "video_id": "vid_456",
      "title": "Highlights: Premier League Week 20",
      "categories": ["sports", "football"],
      "duration_seconds": 600,
      "time_spent_seconds": 450,
      "percentage_watched": 0.75
    }
  }]
}

Feature Comparison: Direct API vs SDK

CapabilitySDKDirect API
Event trackingSimple — helper functions in our Web and CTV SDKs make event tracking straightforwardManual — events must be sent explicitly via API
Engagement tracking (time spent, completion)Automatic — the SDK measures engagement and manages stateManual — your application must measure and track engagement
Identity managementPartially managed — the SDK generates a Permutive ID and collects device IDs where configuredManual — your application must generate, persist, and send user identifiers
Classification Modeled CohortsSupportedUnsupported
Cohort evaluation lookbackSupported — the SDK stores event history on-device, so newly created cohorts can be evaluated against the user’s historical eventsUnsupported
Cohort cachingAutomatic — cohorts are cached in local/device storageManual — your application must cache API responses
Activation to ad platformsFully automatic on Web for supported platforms; on Mobile and CTV, cohorts are exposed via SDK APIs for your application to attach to ad callsManual — extract activations from the response and attach them to ad calls
LatencyReal-time — on-device segmentation with minimal network dependencyNear real-time — dependent on the API round-trip

Verification and Troubleshooting

Verifying Your Integration

After implementing a Direct API deployment, verify each step:
  1. Identity — send a request and confirm the response returns a stable user_id. Send a second request with the same user identifier and confirm the same user_id is returned.
  2. Event collection — create a test cohort in the Permutive dashboard with simple rules (e.g., “any AudioPlay event”) and confirm the user enters it after sending a matching event.
  3. Activations — with activations=true, check that the response includes the expected platforms and cohort IDs.
  4. Contextual signals — call the Contextual API with a known URL and confirm contextual cohorts are returned.
  5. Signal merging — verify that both behavioral and contextual signals appear in your ad requests.
  6. Consent gating — confirm that no CCS API calls are made for unconsented users, and that contextual signals still flow.

Common Issues

Problem: Events are being sent but the user is not entering expected cohorts.Cause: The event name or property structure does not match the schema defined in your Permutive workspace.Solutions:
  • Verify the event name matches exactly (case-sensitive) what is configured in your workspace
  • Check that properties match the expected schema — missing required fields or incorrect types will cause the event to be ignored
  • Use the Permutive dashboard to inspect the event schema and compare it with what your application is sending
  • Ensure nested properties follow the correct structure (e.g., audio.categories should be an array, not a string)
  • For debugging, add synchronous-validation=true as a query parameter to your API call — this forces the CCS API to validate the event schema synchronously and return any schema mismatch errors in the response (by default, validation is asynchronous and schema errors return a 200 response). Use this only for testing — synchronous validation has performance implications and should not be enabled in production.
Problem: The same real user appears as multiple users in Permutive, or cohort history is fragmented across sessions.Cause: Inconsistent identifiers are being sent across requests, or the identifier is not being persisted between sessions.Solutions:
  • Ensure you are using the same user identifier for the same user across all requests
  • If passing a tagged identifier, ensure both the tag and value are identical across all touchpoints
  • If you are generating a Permutive ID on-device, check that it is being read from durable storage on subsequent sessions and not regenerated
  • Verify that the user identifier is configured in your Permutive workspace
Problem: API requests return 401 Unauthorized or 403 Forbidden.Solutions:
  • Find your API key in the Permutive Dashboard under Settings
  • Provide the key as a query parameter (?k=YOUR_API_KEY) or header (X-Api-Key: YOUR_API_KEY)
  • Ensure you are using the key for the correct workspace
  • Check that the key has not been rotated or revoked
Problem: The API returns successfully but cohorts is always an empty array.Solutions:
  • Check that cohorts are configured and active in your Permutive dashboard
  • Verify that the events you are sending match the rules defined in your cohorts
  • Ensure the event properties contain the values that your cohort rules are looking for
  • Allow a few requests to build up user history — some cohorts require multiple events or sessions to qualify
Problem: The response includes cohorts but no activations.Solutions:
  • Ensure you are passing activations=true as a query parameter
  • Check that cohorts are activated for at least one platform in the Permutive dashboard
  • A cohort must be explicitly activated for a platform to appear in the activations map
Problem: API requests are slow or timing out, delaying ad requests.Solutions:
  • Implement a timeout on your API call (e.g., 200-500ms) and fall back to cached cohorts
  • Send API requests as early as possible in the page/screen lifecycle
  • Contact Support if latency persists

Next Steps

CCS API Reference

Full request and response schemas for behavioral cohort segmentation

Contextual API Reference

Request and response schemas for contextual cohort signals

Identity Management

Learn more about Permutive’s identity resolution

SDK Deployment

Consider an SDK if your environment supports it