Skip to content
Create an Audience
.md

Create an Audience

An audience is a definition of who you want to reach. You build it against one or two datasets by selecting filter values that you read from reference data first. This guide covers the single create endpoint, the create-then-poll flow, the fields each dataset type accepts, combining two datasets with an operator, and the optional crossvisitation and cross purchase analyses.

There is one create endpoint:

POST /api/v2/analyses/audiences/create

An audience holds one or two datasets. Each dataset travels in the body as an entry in a datasets array with a type - fetch the accepted values from Get Dataset Types. When you supply two datasets you combine them with an operator (AND, OR, or NOTIN); a single dataset takes no operator. The exact, field-level contract is in the API Reference - Audiences; this guide is the narrative.

Prerequisites

  • A bearer token (Authentication).
  • The reference values you want to target. Read them first with Working with Reference Data - every filter below is populated from a reference read, so you send the exact values the builder expects. The per-type sections link each field to its read.

The create flow

  1. Authenticate and get a bearer token.
  2. Build the dataset body - pick each dataset’s type, the window (start_date / end_date for all types except Cohorts and Demographics), and the filters you want from reference data. With two datasets, also pick an operator.
  3. POST the body to /api/v2/analyses/audiences/create. The response returns the new audience id immediately, in status 100 Initiating.
  4. Poll GET /api/v2/analyses/audiences/{id} until the lifecycle status reaches 104 Completed.

The request body

FieldTypeRequiredDescription
namestringYesAudience name (max 255 chars).
operatorstringConditionalHow to combine two datasets: AND, OR, or NOTIN. Required when datasets holds two blocks; rejected (422) when it holds one. Fetch the accepted values from GET /api/v2/analyses/reference/common/operators.
project_idintegerNoA project id owned by your company to file the audience under.
datasetsobject[]YesOne or two dataset blocks (array of size 1 or 2).
analysesobjectNoThe frequency analyses: frequency (POI, with frequency_day_part), apps_frequency (Apps) and web_frequency (WebDomain) run the analysis the activation preview needs; gated and tied to the dataset type - see Frequency analyses.

Each datasets[i] block always carries a type. Except for Cohorts and Demographics, it also carries a start_date and end_date. The remaining fields depend on the type. The accepted type values are the dataset types your company is entitled to, fetchable from GET /api/v2/analyses/reference/common/dataset-types.

Send only the filters you want - on each type they are optional on their own; you just need enough to define a meaningful population.

Combining two datasets

To target the intersection, union, or exclusion of two populations, supply two dataset blocks and an operator:

  • AND - people who match both datasets.
  • OR - people who match either dataset.
  • NOTIN - people who match the first dataset but not the second.

The operator is required whenever you send two datasets, and is rejected if you send only one. Both datasets are validated exactly the same way (each dataset’s ids must exist). A second dataset that is present but missing its type is rejected with 422.

WebDomain windows roll: start_date must fall within the last 45 days of available data at the moment you send the request - substitute a current window for the dates below.

curl -X POST "https://console.intuizi.com/api/v2/analyses/audiences/create" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: <UNIQUE_KEY>" \
  -d '{
    "name": "US auto-intenders on tech sites",
    "datasets": [
      {
        "type": "WebDomain",
        "start_date": "2026-07-01",
        "end_date": "2026-07-28",
        "iab_category_codes": [1, 4],
        "web_domains": ["example.com"],
        "device_types": ["Mobile"],
        "browsers": ["Chrome"],
        "languages": ["en"],
        "location": { "countries": ["USA"] }
      }
    ]
  }'

A two-dataset request

This example combines a WebDomain dataset with an Apps dataset using AND, so the audience is the people who match both:

WebDomain windows roll: start_date must fall within the last 45 days of available data at the moment you send the request - substitute a current window for the dates below.

curl -X POST "https://console.intuizi.com/api/v2/analyses/audiences/create" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: <UNIQUE_KEY>" \
  -d '{
    "name": "Auto-intenders who also run finance apps",
    "operator": "AND",
    "datasets": [
      {
        "type": "WebDomain",
        "start_date": "2026-07-01",
        "end_date": "2026-07-28",
        "iab_category_codes": [1, 4],
        "location": { "countries": ["USA"] }
      },
      {
        "type": "Apps",
        "start_date": "2026-07-01",
        "end_date": "2026-07-28",
        "categories": [12],
        "location": { "countries": ["USA"] }
      }
    ]
  }'
The Idempotency-Key header is optional and you generate it yourself - any unique string (a UUID v4 is typical), one per logical create. Reuse the same key only when retrying the exact same request: the retry replays the original response (Idempotency-Replayed: true) instead of double-creating the audience; the same key with a different body returns 409.

The response

The create response is the freshly queued audience, in status 100 Initiating with results_count 0. The standard { status, code, message, data } envelope wraps it.

{
  "status": "success",
  "code": 201,
  "message": "Resource created successfully.",
  "data": [
    {
      "id": 88,
      "name": "US auto-intenders on tech sites",
      "status": { "id": 100, "name": "Initiating" },
      "is_cohort": false,
      "results_count": 0,
      "created_at": "2025-01-01 12:00:00"
    }
  ]
}

Dataset fields by type

Every field below is optional unless marked otherwise. The fields backed by a reference catalog link to the read that returns their valid values - fetch those first, then pass the values you collected.

Fields common to all types

These travel directly on each datasets[i] block (except where a type opts out, noted per section).

FieldTypeRequiredDescription
typestringYesThe dataset type. Fetch the types your company is entitled to from Get Dataset Types - a type not enabled for your account is rejected with 422.
start_datestring (Y-m-d)Yes (except Cohorts and Demographics)Window start date.
end_datestring (Y-m-d)Yes (except Cohorts and Demographics)Window end date.
signal_providersstring[]Yes (all types except Cohorts and Demographics)BID values identifying the signal source. Fetch valid values from GET /api/v2/analyses/reference/common/signal-providers.
languagesstring[]NoLanguage codes. Fetch valid values from GET /api/v2/analyses/reference/common/languages.
locationobjectcountries required (all types except Cohorts and Transactions)A { countries[], states[], cities[], dmas[], zipcodes[] } block. countries come from GET /api/v2/analyses/reference/common/countries; states cascade from countries via GET /api/v2/analyses/reference/common/states; cities cascade from states via GET /api/v2/analyses/reference/common/cities; dmas (POI and Origin only) come from GET /api/v2/analyses/reference/common/dmas.

Cohorts takes neither dates nor location. Apps, Demographics, Deidentified and ProfileAttributes read location.countries only; WebDomain and CTV read the full location block. POI requires location.countries and also reads states, cities, dmas and zipcodes. Origin requires location.countries and reads the same states, cities, dmas and zipcodes; its window is widened to whole Monday-to-Sunday weeks. AffinityTransactions is United States only: location.countries is optional there and it reads states, cities and zipcodes; its recency end_date cannot run past the most recent fully-settled purchase week (the latest Sunday at least 11 days before today, UTC, which advances each Thursday). The crosspurchase analysis reads the same purchase data and shares that same end-date cap. WebDomain reads archive-limited visitation data: its start_date must fall within the last 45 days of available data, and an earlier date is rejected with 422. Demographics takes no dates and no signal providers. ProfileAttributes covers whole quarters inside a delivered window.

Web Domain

Targets people by the domains they visit, plus their device and location. Set type to WebDomain.

start_date must fall within the last 45 days of available data: web data lands with a ~48-hour delay, so the window ends two days ago (UTC). Older visitation data is archived and unreadable - an earlier start_date is rejected with 422. The window rolls forward one day at UTC midnight.

FieldTypeRequiredDescription
iab_category_codesinteger[]One of iab_category_codes / web_domainsIAB category ids. Fetch valid values from GET /api/v2/analyses/reference/web/iab-categories (and subcategories from GET /api/v2/analyses/reference/web/iab-subcategories).
web_domainsstring[]One of iab_category_codes / web_domainsWeb domains to target. Domains must exist in the catalog - fetch valid values from GET /api/v2/analyses/reference/web/domains; a domain outside the catalog is rejected with 422.
web_ref_domainsstring[]NoReferrer domains to target. Fetch valid values from GET /api/v2/analyses/reference/web/ref-domains.
device_typesstring[]NoWeb device type names. Fetch valid values from GET /api/v2/analyses/reference/web/device-types.
device_makesstring[]NoWeb device make names. Fetch valid values from GET /api/v2/analyses/reference/web/device-makes.
device_osesstring[]NoWeb device OS names. Fetch valid values from GET /api/v2/analyses/reference/web/device-oses.
browsersstring[]NoWeb browser names. Fetch valid values from GET /api/v2/analyses/reference/web/browsers.
max_devices_per_ipintegerNoMatch strictness: how many devices may share one household/IP. 1 (very strict) to 5 (more reach). Defaults to 3 (recommended).

Connected TV

Targets people by the streaming content and devices they use. Set type to CTV. CTV filters are matched by name, not numeric id.

FieldTypeRequiredDescription
ctv_vendor_namesstring[]NoCTV vendor names. Fetch valid values from GET /api/v2/analyses/reference/ctv/vendors.
ctv_content_type_namesstring[]NoCTV content type names. Fetch valid values from GET /api/v2/analyses/reference/ctv/content-types.
ctv_content_genre_namesstring[]NoCTV content genre names. Fetch valid values from GET /api/v2/analyses/reference/ctv/content-genres.
ctv_channel_namesstring[]NoCTV channel names. Fetch valid values from GET /api/v2/analyses/reference/ctv/channel-names.
iab_codesarrayNoIAB codes. Fetch valid values from GET /api/v2/analyses/reference/web/iab-categories.
ctv_device_typesstring[]NoCTV device type names. Fetch valid values from GET /api/v2/analyses/reference/ctv/device-types.
ctv_device_makesstring[]NoCTV device make names. Fetch valid values from GET /api/v2/analyses/reference/ctv/device-makes.
ctv_device_osesstring[]NoCTV device OS names. Fetch valid values from GET /api/v2/analyses/reference/ctv/device-oses.
ctv_connection_typesstring[]NoCTV connection type names. Fetch valid values from GET /api/v2/analyses/reference/ctv/connection-types.
ctv_ispsstring[]NoCTV ISP names. Fetch valid values from GET /api/v2/analyses/reference/ctv/isps.
ctv_seriesstring[]NoCTV series names. Fetch valid values from GET /api/v2/analyses/reference/ctv/series.

Cohorts

Reuses one of your own completed cohorts as an audience. Set type to Cohorts. Cohorts are company-scoped: you can only target a completed cohort that belongs to your company. This type takes no dates and no location.

FieldTypeRequiredDescription
cohort_idintegerYesA completed cohort id owned by your company. Fetch valid ids from GET /api/v2/analyses/reference/cohorts/get-cohorts.

Apps

Targets people by the mobile apps they have and the categories those apps belong to. Set type to Apps. Apps reads location.countries only.

FieldTypeRequiredDescription
app_idsinteger[]At least one of categories / taxonomies / bundle_ids / app_idsApp ids to target. Fetch valid ids from GET /api/v2/analyses/reference/apps/bundle-ids.
bundle_idsstring[]At least one of categories / taxonomies / bundle_ids / app_idsApp bundle ids to target. Fetch valid values from GET /api/v2/analyses/reference/apps/bundle-ids.
categoriesarrayAt least one of categories / taxonomies / bundle_ids / app_idsApp category ids. Fetch valid values from GET /api/v2/analyses/reference/apps/categories.
taxonomiesarrayAt least one of categories / taxonomies / bundle_ids / app_idsApp taxonomy ids. Fetch valid values from GET /api/v2/analyses/reference/apps/taxonomies.
device_osstring[]NoApp device OS values. Fetch valid values from GET /api/v2/analyses/reference/apps/os.
refineobjectNoPost-build refinement: keep only the most active devices. Permission-gated, single-dataset audiences only - see Refine.

App tags narrow the targeted set; fetch valid values from GET /api/v2/analyses/reference/apps/tags.

Apps location supports countries only: sending location.states, location.cities, location.dmas, or location.zipcodes on an Apps dataset is rejected with 422.

POI

Targets people by the points of interest (POIs) they physically visited. Set type to POI. The POI catalogs form a cascade - pick segments, use them to read categories, categories to read brands, and brands to read individual locations:

segments -> categories (?segments[]) -> brands (?categories[]) -> locations (?brands[])

Unlike the other dataset types, POI requires location.countries (at least one country). The dataset also accepts an optional time-of-day window (time_limits) and a distance radius (distance_limits).

FieldTypeRequiredDescription
categoriesinteger[]At least one of categories / analysisdata / locationsPOI category ids. Fetch valid values from GET /api/v2/analyses/reference/poi/categories (cascades from segments read via GET /api/v2/analyses/reference/poi/segments).
analysisdatainteger[]At least one of categories / analysisdata / locationsPOI brand ids. Fetch valid values from GET /api/v2/analyses/reference/poi/brands (cascades from categories).
locationsarrayAt least one of categories / analysisdata / locationsPOI location identifiers. Fetch candidates from GET /api/v2/analyses/reference/poi/locations (cascades from brands). How the values are interpreted is set by location_identifier_type.
location_identifier_typestringNoHow locations are interpreted: none, location_id, external_id, store_id, placekey, h3_index, or h3_index_integer.
signal_providersstring[]YesBID values identifying the signal source. Fetch valid values from GET /api/v2/analyses/reference/common/signal-providers.
location.countriesstring[]YesCountry codes (at least one). Fetch from GET /api/v2/analyses/reference/common/countries.
location.statesstring[]NoState codes. Cascade from countries via GET /api/v2/analyses/reference/common/states.
location.citiesstring[]NoCity names. Cascade from states via GET /api/v2/analyses/reference/common/cities.
location.dmasstring[]NoDMA market labels. Fetch from GET /api/v2/analyses/reference/common/dmas.
location.zipcodesstring[]NoZIP codes. Cascade from cities via GET /api/v2/analyses/reference/common/zipcodes (paginated).
time_limitsobjectNo{ process: boolean, start: 0-23, end: 0-23 } - restrict to an hour-of-day window when process is true.
day_limitsobjectNo{ process: boolean, days: integer[] } - restrict to days of the week when process is true; ISO numbering (1 = Monday .. 7 = Sunday).
distance_limitsobjectNo{ process: boolean, distance: number, unit: "miles" | "kilometers" } - restrict to visits within a radius when process is true.
refineobjectNoPost-build refinement: keep only the most active devices. Permission-gated, single-dataset audiences only - see Refine.

The console’s audience-level quality filters (anomalous devices, GPS-only signals, anomalous POIs, GPS decimals) are not exposed on this API and default to off.

Transactions

Targets people by what they actually bought. Set type to AffinityTransactions. The purchase catalogs form a cascade - pick categories, use them to read sub-categories, and both to read brands:

categories -> subcategories (?categories[]) -> brands (?categories[], ?subcategories[])

At least one categories or brands value is required - without one the dataset has no purchase filter. Sub-categories are the merchant category description strings the sub-categories read returns (not ids); picked without any brands, they are resolved to the brands they cover.

Affinity purchase data is United States only, so location.countries is optional here and always stored as ["USA"]. Sending any other country code is rejected with 422, as is location.dmas - this type reads location.states, location.cities and location.zipcodes only.

FieldTypeRequiredDescription
categoriesinteger[]At least one of categories / brandsAffinity purchase category ids. Fetch valid values from GET /api/v2/analyses/reference/affinity-transactions/categories.
brandsstring[]At least one of categories / brandsAffinity brand id strings. Fetch valid values from GET /api/v2/analyses/reference/affinity-transactions/brands (cascades from categories and sub-categories).
subcategoriesstring[]NoSub-category strings cascading from categories. Fetch valid values from GET /api/v2/analyses/reference/affinity-transactions/subcategories.
incomesstring[]NoIncome bands. Fetch valid values from GET /api/v2/analyses/reference/affinity-transactions/incomes.
agesstring[]NoAge bands. Fetch valid values from GET /api/v2/analyses/reference/affinity-transactions/ages.
gendersstring[]NoGender values. Fetch valid values from GET /api/v2/analyses/reference/affinity-transactions/genders.
ethnicitiesstring[]NoEthnicity values. Fetch valid values from GET /api/v2/analyses/reference/affinity-transactions/ethnicities.
channelsstring[]NoPurchase channel: B&M (in-store) and/or ONLINE.
spend_minnumberNoMinimum total spend (USD) across the date window.
spend_maxnumberNoMaximum total spend (USD) across the date window.
txn_minnumberNoMinimum number of transactions across the date window.
txn_maxnumberNoMaximum number of transactions across the date window.
analysis_spend_frequencybooleanNoAlso compute the spend distribution for the built audience. Defaults to false.
analysis_transactions_frequencybooleanNoAlso compute the transaction-count distribution for the built audience. Defaults to false.
max_devices_per_ipintegerNoMatch strictness: how many devices each shopper may expand to. 1 (very strict) to 5 (more reach). Defaults to 3 (recommended).
{
  "type": "AffinityTransactions",
  "start_date": "2025-01-01",
  "end_date": "2025-03-31",
  "signal_providers": ["BID001"],
  "categories": [1],
  "subcategories": ["Eating Places, Restaurants"],
  "brands": ["501"],
  "incomes": ["100k-150k"],
  "channels": ["B&M"],
  "spend_min": 50,
  "txn_min": 2,
  "max_devices_per_ip": 3,
  "location": { "states": ["CA"] }
}

Demographics

Targets people by their household demographic attributes. Set type to Demographics. The four dictionaries are flat - there is no cascade - and at least one of them must carry a selection, otherwise the dataset has no filter.

This type carries no date window and no signal providers: sending start_date, end_date or signal_providers is rejected with 422. Its location supports countries only (at least one is required), so location.states, location.cities, location.zipcodes and location.dmas are rejected too.

FieldTypeRequiredDescription
gendersstring[]At least one of genders / ages / marital_statuses / incomesGender values. Fetch valid values from GET /api/v2/analyses/reference/demographics/genders.
agesstring[]At least one of genders / ages / marital_statuses / incomesAge ranges. Fetch valid values from GET /api/v2/analyses/reference/demographics/ages.
marital_statusesstring[]At least one of genders / ages / marital_statuses / incomesMarital status values. Fetch valid values from GET /api/v2/analyses/reference/demographics/marital-statuses.
incomesstring[]At least one of genders / ages / marital_statuses / incomesIncome ranges. Fetch valid values from GET /api/v2/analyses/reference/demographics/incomes.
location.countriesstring[]YesCountry codes (at least one). Fetch valid values from GET /api/v2/analyses/reference/common/countries.
{
  "type": "Demographics",
  "genders": ["F"],
  "ages": ["25-34", "35-44"],
  "incomes": ["100k-150k"],
  "location": { "countries": ["USA"] }
}

Deidentified

Delivers the deidentified signals themselves for a country and date window, rather than a targeted device list. Set type to Deidentified. Instead of filters it takes fields: which signal fields the delivery carries.

fields is an object keyed by group (geoLocation, advertising, userDetails, ipDetails, privacy, general). Every group is optional - send only the groups you want, or omit fields entirely for the default shape. Fetch the fields and the group each belongs to from GET /api/v2/analyses/reference/deidentified/fields; an unknown field, or a field sent under a group it does not belong to, is rejected with 422.

This type is delivered on its own: an audience whose dataset is Deidentified takes exactly one dataset block, so pairing it with a second one (and therefore an operator) is rejected with 422.

Its location supports countries only, so location.states, location.cities, location.zipcodes and location.dmas are rejected. When location.countries includes USA, the date window may cover at most two weeks per delivery.

FieldTypeRequiredDescription
fieldsobjectNoWhich signal fields the delivery carries, keyed by group. Fetch the fields and their groups from GET /api/v2/analyses/reference/deidentified/fields.
signal_providersstring[]YesBID values identifying the signal source (at least one). Fetch valid values from GET /api/v2/analyses/reference/common/signal-providers.
start_datestring (Y-m-d)YesWindow start.
end_datestring (Y-m-d)YesWindow end. At most two weeks after start_date when location.countries includes USA.
location.countriesstring[]YesCountry codes (at least one). Fetch valid values from GET /api/v2/analyses/reference/common/countries.
{
  "type": "Deidentified",
  "start_date": "2026-01-05",
  "end_date": "2026-01-18",
  "signal_providers": ["BID001"],
  "fields": {
    "geoLocation": ["latitude", "longitude"],
    "ipDetails": ["ipisp"],
    "general": ["d_utc"]
  },
  "location": { "countries": ["USA"] }
}

Profile Attributes

Targets people by the profile attributes they carry. Set type to ProfileAttributes. profile_attributes is a list of attribute rows - each one a category, a key under that category, and the values of that key to match - combined with AND, so a device must match every row.

The attribute catalogs form a three-level cascade - pick a category, use it to read the keys under it, and both to read that key’s values:

categories -> keys (?category_ids[]) -> values (?category_ids[], ?key)

Each row is checked at all three levels: an unknown category, a key that does not belong to the row’s category, or a value that does not belong to the row’s category and key is rejected with 422 naming the exact row.

Its location supports countries only, so location.states, location.cities, location.zipcodes and location.dmas are rejected.

Profile attribute data is delivered quarterly. start_date and end_date must fall inside the delivered window published by GET /api/v2/analyses/reference/profile-attributes/recency-limits; a date outside it is rejected with 422. The stored window is expanded out to whole quarters - start_date to the first day of its quarter and end_date to the last day of its quarter.

FieldTypeRequiredDescription
profile_attributesobject[]YesAttribute rows (at least one), combined with AND.
profile_attributes[].category_idintegerYesAn attribute category id. Fetch valid values from GET /api/v2/analyses/reference/profile-attributes/categories.
profile_attributes[].keystringYesAn attribute key under that category. Fetch valid values from GET /api/v2/analyses/reference/profile-attributes/keys (cascades from categories).
profile_attributes[].value_idsinteger[]YesValue ids of that key (at least one). Fetch valid values from GET /api/v2/analyses/reference/profile-attributes/values (cascades from categories and the key).
signal_providersstring[]YesBID values identifying the signal source (at least one). Fetch valid values from GET /api/v2/analyses/reference/common/signal-providers.
start_datestring (Y-m-d)YesWindow start, inside the delivered window.
end_datestring (Y-m-d)YesWindow end, inside the delivered window.
location.countriesstring[]NoAccepted but never applied - this dataset has no geography. Safe to omit.
{
  "type": "ProfileAttributes",
  "start_date": "2026-04-01",
  "end_date": "2026-06-30",
  "signal_providers": ["BID001"],
  "profile_attributes": [
    { "category_id": 1, "key": "auto_intent", "value_ids": [10, 12] },
    { "category_id": 2, "key": "credit_band", "value_ids": [30] }
  ]
}

Origin

Targets people by their home location - where a device originates - rather than by the places it visited. Set type to Origin. Each device’s home is resolved once per week and placed by point-in-polygon against the ZIP code and DMA boundaries, so every filter narrows that resolved home location.

Its only filters are geographic: location.countries is required, and states, cities, dmas and zipcodes are optional. signal_providers is required as for the other dated types.

Its data is weekly - a device’s home is resolved once per week, and there is no day in the data - so a sub-week window cannot exist. Any dates are accepted, and the window is widened to the whole Monday-to-Sunday weeks it touches: Tuesday to Friday builds, and is stored as, that full week.

Origin data exists for a limited set of countries. Fetch the covered set from GET /api/v2/analyses/reference/common/countries with ?datasetType=Origin; a country outside it is rejected with 422.

FieldTypeRequiredDescription
signal_providersstring[]YesBID values identifying the signal source (at least one). Fetch valid values from GET /api/v2/analyses/reference/common/signal-providers with ?dataType=Origin.
start_datestring (Y-m-d)YesWindow start. Widened back to the Monday of its week.
end_datestring (Y-m-d)YesWindow end, on or after start_date. Widened forward to the Sunday of its week.
location.countriesstring[]YesCountry codes (at least one) with Origin coverage. Fetch valid values from GET /api/v2/analyses/reference/common/countries with ?datasetType=Origin.
location.statesstring[]NoState codes. Fetch valid values from GET /api/v2/analyses/reference/common/states.
location.citiesstring[]NoCity names. Fetch valid values from GET /api/v2/analyses/reference/common/cities.
location.dmasstring[]NoDMA labels exactly as returned by GET /api/v2/analyses/reference/common/dmas - the label string, never a numeric code.
location.zipcodesstring[]NoZIP codes. Fetch valid values from GET /api/v2/analyses/reference/common/zipcodes.
{
  "type": "Origin",
  "start_date": "2026-08-31",
  "end_date": "2026-09-13",
  "signal_providers": ["BID001"],
  "location": {
    "countries": ["USA"],
    "states": ["CA"],
    "dmas": ["803 - Los Angeles, CA"]
  }
}

Refine the audience

POI and Apps datasets accept an optional refine object that refines the audience after it is built: devices are ranked by a behavioral metric and only a subset is kept - the top X% (percentile), everyone at or above a minimum event count (threshold), or the top N devices (rank). The refined audience keeps its full data shape; results_count and every downstream activation reflect the refined device set.

Refine applies to single-dataset audiences only - a refine block on either dataset of a two-dataset audience, or on any other dataset type, is rejected with 422.

Refine is a gated feature. It requires additional permissions that your Account Manager can enable for your account. If a refine block is present and the feature is not enabled, the request is rejected with 403 - it is never silently ignored. Omitting the block entirely requires nothing.
FieldTypeRequiredDescription
typestringYesHow the subset is selected: percentile (keep the top X% by the metric), threshold (keep devices with at least value events), or rank (keep the top value devices).
metricstringYesThe behavioral metric to rank devices by. POI: visit_count or visit_days. Apps: app_usage_count or app_usage_days.
valuenumberYesMeaning depends on type: the percentage to keep for percentile (greater than 0 and less than 100), the minimum event count for threshold, or the number of devices for rank (whole number, at least 1).
fraud_cutoff_percentileintegerNoDrop outlier devices above this activity percentile (1-100, e.g. 99) before the cut, so bot-like devices do not crowd out the subset you keep.
max_valueintegerNoUpper bound on the metric, threshold type only - keeps devices between value and max_value events. Rejected with 422 on the other types.

For example, to keep only the devices in the top 10% by visit frequency, dropping the top 1% as likely bots:

{
  "name": "Frequent coffee shop visitors",
  "datasets": [{
    "type": "POI",
    "start_date": "2025-01-01",
    "end_date": "2025-03-31",
    "signal_providers": ["BID001"],
    "categories": [101],
    "location": { "countries": ["USA"] },
    "refine": {
      "type": "percentile",
      "metric": "visit_count",
      "value": 10,
      "fraud_cutoff_percentile": 99
    }
  }]
}

Crossvisitation

Crossvisitation narrows the audience to people who physically visited points of interest (POIs) you select - by category, by brand, or by specific locations - within a date window and optional time-of-day and distance limits. It travels in the body as an optional top-level crossvisitation object alongside datasets.

Crossvisitation is a gated feature. It requires additional permissions that your Account Manager can enable for your account. If a crossvisitation block is present and the feature is not enabled, the request is rejected with 403 - it is never silently ignored. Omitting the block entirely requires nothing.

Where the POI ids come from

Pick the POI values from the read-only Dataset Types - POI catalogs - the same source as every other audience-builder field. (Creating and managing the underlying POI data itself is the separate My POI Data surface; see Submit POI Data.)

Crossvisitation fields

FieldTypeRequiredDescription
start_datestring (Y-m-d)YesStart of the visitation window.
end_datestring (Y-m-d)YesEnd of the visitation window.
poi_levelbooleanNoResolve at the individual-POI level rather than the parent brand/category.
filtersstring[]NoQuality filters. Any of anomalous_devices, gps_only, anomalous_pois.
categoriesinteger[]Yes (exactly one)A single POI category id (see above). The block requires exactly one category.
brandsinteger[]NoPOI brand ids (see above).
locationsarrayNoPOI location identifiers. Their kind is set by location_identifier_type.
location_identifier_typestringNoHow the locations values are interpreted: none, location_id, external_id, store_id, placekey, h3_index, or h3_index_integer.
locationobjectcountries requiredGeographic scope: { countries[], states[], cities[], dmas[], zipcodes[] } - countries needs at least one entry, the rest are optional. countries come from GET /api/v2/analyses/reference/common/countries.
time_limitsobjectNo{ process: boolean, start: 0-23, end: 0-23 } - restrict to an hour-of-day window when process is true.
day_limitsobjectNo{ process: boolean, days: integer[] } - restrict to days of the week when process is true; ISO numbering (1 = Monday .. 7 = Sunday).
distance_limitsobjectNo{ process: boolean, distance: number, unit: "miles" | "kilometers" } - restrict to visits within a radius when process is true.

A crossvisitation example

Crossvisitation is available only for the POI dataset. This combines a POI dataset with a crossvisitation block that narrows it to visitors of specific POI brands within a date window and a distance limit:

curl -X POST "https://console.intuizi.com/api/v2/analyses/audiences/create" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: <UNIQUE_KEY>" \
  -d '{
    "name": "Coffee-shop cross visitors",
    "datasets": [
      {
        "type": "POI",
        "start_date": "2025-01-01",
        "end_date": "2025-03-31",
        "signal_providers": ["BID001"],
        "categories": [10],
        "location": { "countries": ["USA"] }
      }
    ],
    "crossvisitation": {
      "start_date": "2025-01-01",
      "end_date": "2025-03-31",
      "poi_level": true,
      "filters": ["gps_only"],
      "categories": [10],
      "brands": [55, 56],
      "location": { "countries": ["USA"] },
      "distance_limits": { "process": true, "distance": 5, "unit": "miles" }
    }
  }'
Crossvisitation shapes how the audience is built, but it is not echoed back on the read endpoints. GET /api/v2/analyses/audiences/{id} returns the datasets and operator, not the crossvisitation block. Keep your own copy of what you sent.

Cross Purchase

Cross purchase runs the built audience against the affinity purchase data: the audience’s devices are matched into purchase transactions within your date window, scoped to the target categories, sub-categories and brands you select. It travels in the body as an optional top-level crosspurchase object alongside datasets. Because it runs on the finished audience, it is available with any dataset combination - one or two datasets, any type. The analysis results are surfaced in the Audience Manager alongside the audience.

Cross purchase is a gated feature. It requires additional permissions that your Account Manager can enable for your account. If a crosspurchase block is present and the feature is not enabled, the request is rejected with 403 - it is never silently ignored. Omitting the block entirely requires nothing.

Pick the target values from the read-only Transactions catalogs - they cascade (categories -> subcategories -> brands), and any mix works: at least one target across the three arrays is required.

FieldTypeRequiredDescription
start_datestring (Y-m-d)YesStart of the purchase window.
end_datestring (Y-m-d)YesEnd of the purchase window. The range may cover at most 2 calendar months.
target_categoriesinteger[]At least one of target_categories / target_subcategories / target_brandsAffinity category ids. From GET /api/v2/analyses/reference/affinity-transactions/categories.
target_subcategoriesstring[]At least one of target_categories / target_subcategories / target_brandsAffinity sub-category strings. From GET /api/v2/analyses/reference/affinity-transactions/subcategories.
target_brandsstring[]At least one of target_categories / target_subcategories / target_brandsAffinity brand id strings. From GET /api/v2/analyses/reference/affinity-transactions/brands.

For example, to report how a POI audience purchases across dining brands:

{
  "name": "Coffee visitors vs dining purchases",
  "datasets": [{
    "type": "POI",
    "start_date": "2025-01-01",
    "end_date": "2025-03-31",
    "signal_providers": ["BID001"],
    "categories": [101],
    "location": { "countries": ["USA"] }
  }],
  "crosspurchase": {
    "start_date": "2025-01-01",
    "end_date": "2025-02-28",
    "target_categories": [1],
    "target_brands": ["501"]
  }
}

Poll until Completed

Audience creation is asynchronous (see The Async Model). The create response gives you the new audience id; poll it until its lifecycle status reaches 104 Completed.

curl "https://console.intuizi.com/api/v2/analyses/audiences/<AUDIENCE_ID>" \
  -H "Authorization: Bearer <YOUR_TOKEN>" \
  -H "Accept: application/json"
  • 100-103, 105, 108, 109 - still processing. Poll again shortly. 109 Visualizing data streams means the audience is drawing the data stream visualizations it opted into, and 105 DataStreaming comes before 104.
  • 104 Completed - the audience is ready to activate.
  • 107 Additional Info - the build stopped and will not continue. Stop polling. Audience Manager shows the reason, most often a date range outside the data available for the dataset. Fix the request and create the audience again.
  • 4xx - an error state. Stop polling and inspect the response.

See List, Get & Delete for reading and removing audiences, and Read an Audience for the full read response.

Next steps

Reference