Skip to content
Last updated

Unified ID 2.0 Workflow

This guide provides technical instructions for data engineers to configure and operate the predefined UID2 workflow within the Treasure Data platform.

The primary goal is to take your first-party Personally Identifiable Information (PII), such as email addresses and phone numbers, and convert it into privacy-safe Unified ID 2.0 (UID2) identifiers for use in the advertising ecosystem.

What is UID2?

From The Trade Desk - UID2 Overview | Unified ID 2.0 UID2 is a framework that enables deterministic identity for advertising opportunities on the open Internet for many participants across the advertising ecosystem. The UID2 framework enables logged-in experiences from publisher websites, mobile apps, and Connected TV (CTV) apps to monetize through programmatic workflows. Built as an open-source, standalone solution with its unique namespace, the framework offers the user transparency and privacy controls designed to meet local market requirements.

Some of the business values from this integration are below.

  • Use a privacy-conscious form of customer data in media-buying platforms.
  • Manage frequency and suppress audiences across channels and devices.
  • Support identity use cases where cookies don’t exist.
  • Offer opt-out, with the goal of improving consumer privacy controls.

Prerequisites

Terminology

TermDescription
DIIDII is Directly Identifying Information, currently Email Addresses and Phone Numbers.
DII NormalizationDII (email address & phone numbers) must be normalized to the expected format per Normalization Standards. All DII will be mapped by a UID2 Service Operator as long as it is in the expected format as detailed below. Note that the UID2 Service Operator will map any email address or phone number as long as it is in the expected format, the email/phone does not need to be an actual live or working DII. - Email addresses must be normalized per the Email Normalization Standard. - Phone numbers must be in E.164 format. - Timestamps must be in ISO 8601 format.

Request to Access UID2.0

Before setting up this connector to start UID2 integrations, users need to have access to UID2 by submitting an access request through https://unifiedid.com/request-access.

Please work with your Trade Desk Account Manager or the Marketing Agency that owns the Trade Desk Account.

Once users have access to the UID2 service, they will have UID2 credentials that are an API key and a client secret to access the UID2.

You can get more information at https://unifiedid.com/docs/getting-started/gs-credentials#api-key-and-client-secret

AUID2 Base URL:

Obtain Trade Desk Account Credentials

Users can find the advertiser ID and secret key from the management console within The Trade Desk. Open “Preferences”, and then “First Party Data Credentials” to view the advertiser ID and secret key.

  • Advertiser ID: The advertiser ID for your account with The Trade Desk.
  • Advertiser secret: The advertiser secret for your account with The Trade Desk.

Prepare normalized(and/or hashed) DII

The UID2 service accepts DII(email and phone number currently) to convert UID2. Each piece of identifying information should follow a specific data format for conversion. Please review the Terminology section or https://unifiedid.com/docs/getting-started/gs-normalization-encoding for more details.

How the Workflow works

The UID2 workflow automates the process of generating and maintaining UID2s. At a high level, the process is as follows:

  • Input:
    • The workflow reads hashed or unhashed email addresses and phone numbers from source tables you specify within your Treasure Data database.
  • Processing:
    • It communicates with the UID2 Operator Service to securely map your PII to UID2s.
    • The workflow automatically detects UID2s that have expired or reached their refresh_from time (i.e., are considered stale by the UID2 service) and remaps them to fresh, active UID2s.
  • Output:
    • The final, valid UID2s are written to a central table in your database (ttd_uid2_ids)

Requirements and Limitations

There is a rate limit in the UID2 API endpoint; customers should consider limiting the number of records to process by setting the parameter uid2_limit_record

Setup and Configuration

Prepare Database Tables

The workflow requires several tables to operate. You must create some, while others are managed automatically by the workflow.

The API in your target database creates the necessary staging tables for API responses.

  • ttd_uid2_resp: a staging table that hold the response of uid2 mapping

Or you can run the following SQL queries

CREATE TABLE IF NOT EXISTS "ttd_uid2_resp"("identifier" varchar);

Tables Automatically Created by the Workflow

  • ttd_uid2_ids: The main output table containing your source data mapped to its current, valid UID2.
  • ttd_uid2_ids_archive: An archive of records from ttd_uid2_ids.
  • ttd_uid2_rqst: A staging table that holds the JSON payloads for batch requests sent to the UID2 Operator.

Create the Workflow via API

We can create a predefined UID2 using the API

API Request

curl -L 'https://integrations-gateway.us01.treasuredata.com/integration_workflow/workflows/uid2/' \
-H 'Authorization: {Your_TD_API_KEY}' \
-H 'Content-Type: application/json' \
--data-raw '{
    "type": "uid2",
    "plugin_config": {
      "type": "uid2",
      "host": "https://operator-integ.uidapi.com",
      "apikey": "****",
      "secret": "****",
      "td_apikey": "****",
      "database": "your_database",
      "td_host": "api-production.treasuredata.com",
      "uid2_limit_records": 200000000,
      "enable_auto_format": true,
      "skip_limit": 500,
      "mapping_uid2_bulkload_session_name": "uid2_bulkload_session_name",
      "td_uid2_src_lst": [
        {
          "src_db": "src_db",
          "src_tbl": "src_tbl",
          "src_id_col": "contact_id",
          "src_dii_col": "email",
          "src_dii_typ": "EMAIL"
        }
      ],
      "custom_batch_size": 5000,
      "custom_email_filter_import_uid2_identifier": "regexp_like(identifier, '''^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$''')",
      "custom_phone_filter_import_uid2_identifier": "NOT regexp_like(identifier, '''^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$''')"
    },
    "workflow_config": {
      "project_name": "project_name",
      "revision": "revision",
      "schedule": "daily>: 07:00:00",
      "timezone": "America/Los_Angeles"
    }
  }'

API Response

{
  "id": "your workflow id",
  "name": "your workflow project name",
  "revision": "your workflow project revision",
  "createdAt": "2025-07-01T09:55:28Z",
  "updatedAt": "2025-07-03T07:23:35Z",
  "deletedAt": null,
  "archiveType": "s3",
  "archiveMd5": "z2c8UL26Vl+uJGfR9wPmKA==",
  "metadata": []
}

Parameters Reference

Workflow config

NameDescriptionValueDefault ValueRequired
project_nameProject name for workflow Please use a different project_name for each UID2 setupN/AN/AYes
revisionProject revision Using a different revision may override the existing project if there is a project with the same nameN/AN/AYes
scheduleSchedule for workflow Should be daily with time You can find support values and detailed instructions in Treasure Data’s documentation here: 🔗 Scheduling Workflows – Treasure Data DocsN/AN/AYes
timezoneThe timezone that the schedule should be set up inN/AUTCNo
Plugin config
NameDescriptionValueDefault ValueRequired
typeThe plugin typeuid2N/AYes
hostThe uid2 host Get from https://unifiedid.com/docs/getting-started/gs-environmentsN/AN/AYes
apikeyThe UID2 API keyN/AN/AYes
secretThe UID2 API client secretN/AN/AYes
databaseYour database name in TDN/AN/AYes
td_apikeyYour TD API keyN/AN/AYes
td_hostTD API hostN/AN/AYes
mapping_uid2_bulkload_session_nameYour previous bulkload mapping session. Use when you want to migrate to a new workflow but keep the bulkload session for incremental loadN/AN/ANo
td_uid2_src_lstList of DII table, imported for mapping UID2N/AN/AYes
skip_limitThe maximum number of invalid DII that  we can ignore  and process furtherN/A500No
uid2_limit_recordsThe maximum number of DII that are sent to map UID2N/A2.000.000.000No
custom_batch_sizeThe number of DII groups in one requestN/A10000No
enable_auto_formatEnable this option to auto-hash the DII email valueN/AtrueNo
custom_email_filter_import_uid2_identifierFormat validation rule for an email. Emails that meet the validation rules will be used for converting to UID2. Format validation rule for email. Emails that meet the validation rules will be used for converting to UID2.N/Aregexp_like(identifier, '''^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$''')No
custom_phone_filter_import_uid2_identifierFormat validation rule for a phone number. Phone numbers that meet the validation rules will be used for converting to the UID2Format validation rule for phone numbers. Phone numbers that meet the validation rules will be used for converting to UID2.N/ANOT regexp_like(identifier, '''^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$''')No
custom_email_hash_filter_import_uid2_identifierFormat validation rule for a hashed email. Hashed emails that meet the validation rules will be used for converting to the UID2 format validation rule for hashed email. Hashed emails that meet the validation rules will be used for converting to UID2N/Aregexp_like(identifier, '^[a-zA-Z0-9+/]+={0,2}$')No
custom_phone_hash_filter_import_uid2_identifierFormat validation rule for a hashed phone number. Hashed phone numbers that meet the validation rules will be used for converting to UID2.Format validation rule for a hashed phone number. Hashed phone numbers that meet the validation rules will be used for converting to UID2.N/Aregexp_like(identifier, '^[a-zA-Z0-9+/]+={0,2}$')No
last_uid2_request_timeStore the last UID2 request time for the next run. That value is used to select records in the ttd_uid2_rqst tableN/AN/ANo
last_uid2_request_indexStore the last UID2 request index for the next run. That value is used to select records in the ttd_uid2_rqst tableN/AN/ANo
Dii source list
NameDescriptionValueDefault ValueRequired
src_dbDII source databaseN/AN/AYes
src_tblDII source tableN/AN/AYes
src_id_col ID Column if available If no ID column, use the literal term "null" (w/o quotes)​N/AN/AYes
src_dii_colThe name of the Source DII column can be any nameN/AN/AYes
src_dii_typ The type of DII - EMAIL - PHONE - EMAIL_HASH - PHONE_HASH N/AN/AYes

Monitor the Workflow

Once created, you can access and monitor the workflow from the TD Console:

  1. Navigate to
  2. Data Workbench > Workflows.
  3. Search for the project_name you defined in the API call.

Please select the project to view its

Run History, where you can check the status, duration, and logs for each run.

Database Tables Explained

ttd_uid2_ids table

– Transactional Table – Main TTD UID2 Table

COLUMNTYPEDESCRIPTION
timeINTEGERUnixtime of record INSERT
src_dbVARCHARThe source database of the DII value
src_tblVARCHARThe source table of the DII value
src_id_colVARCHARThe ID column for the source table of the DII value
src_idVARCHARThe ID value of the record in the source table of the DII value
src_colVARCHARThe Source column in the source table of the DII value
src_typVARCHARThe type of DII, one of {EMAIL, PHONE}
src_dataVARCHARThe source DII value
advertising_idVARCHARThe UID2 value (Defined as advertising_id in Service Operator Service APIs)
refresh_fromINTEGERThe time in epoch when the UID should be rotated. Start with 0.
is_currentINTEGERIndicates whether the UID2 (advertising_id column) contains a current UID2 value mapped from DII. - 0 (zero) – NO – Indicates that the ttd_uid2_ids record is new or does not have a current UID2 value in the advertising_id column. In either case, a new UID2 must be fetched from The Trade Desk (TTD). - 1 (one) – YES – Indicates that the ttd_uid2_ids record has a current UID2 value in the advertising_id column, so a new UID2 does NOT need to be fetched from TTD. The is_current state is managed during each workflow (WF) run and should always have the value 1 (one) for all records after every successful WF run. If any records have the value 0 (zero) after the WF run has completed, that means that something failed. The two primary causes of DII ↔︎ UID2 mapping failure are: - The DII format is not correct and therefore cannot be mapped by the UID2 Service Operator. For example, the email is not a valid email format (the domain is missing a TLD extension) and cannot be mapped by the Operator. Phone numbers must be in a valid E.164 format. Note that the Operator will map any email address or phone number as long as it is in the expected format; the email/phone does not need to be an actual live or working DII. - The TD UID2 Mapping Workflow failed for any reason.

ttd_uid2_ids_archive – Transactional Table – Main UID2 Table

Same schema as ttd_uid2_ids table, except that the is_current will always have the value -1 to indicate archive records.

COLUMNTYPEDESCRIPTION
timeINTEGERUnixtime of record INSERT
src_dbVARCHARThe source database of the DII value
src_tblVARCHARThe source table of the DII value
src_id_colVARCHARThe ID column for the source table of the DII value
src_idVARCHARThe ID value of the record in the source table of the DII value
src_colVARCHARThe Source column in the source table of the DII value
src_typVARCHARThe type of DII, one of {EMAIL, PHONE}
src_dataVARCHARThe source DII value
advertising_idVARCHARThe TTD UID2 value (Defined as advertising_id in Operator Service API's)
refresh_fromINTEGERUnix epoch seconds; refresh/remap when current time >= refresh_from. 0 indicates no refresh scheduled yet.
is_currentINTEGERAlways has the value -1 (negative-one) to indicate archived records.

ttd_uid2_rqst – Staging Table – For UID Map API Requests

COLUMNTYPEDESCRIPTION
timeINTEGERUnixtime of record INSERT
rnk_numLONGThe sequence number of this UID2 Service Operator API batch request
ttd_uid2_rqstVARCHARThe actual JSON payload for the UID2 Service Operator API batch request. It is a logical and valid JSON, stored  VARCHAR for simplicity and convenience. It is stored as plain-text unencrypted; the TD Python client script manages all security and encryption/decryption internally.

ttd_uid2_resp – Staging Table – For UID2 Map API Responses

COLUMNTYPEDESCRIPTION
timeINTEGERUnixtime of record INSERT
rnk_numLONGThe sequence number of this UID2 Service Operator API batch request (not used by this WF, for analysis purposes).
identifierVARCHARThe DII value, either an Email or Phone
advertising_idVARCHARThe UID2 value (Defined as advertising_id in Operator Service API's)
refresh_fromINTEGERUnix timestamp in seconds (epoch seconds) indicating the earliest time from which this UID2 should be refreshed/rotated. A value of 0 means no refresh time is set or provided.
unmapped_reasonVARCHARThe unmap reason for UID2 mapping

Next Step

After the UID2 workflow runs successfully, users will have UID2 in the specified destination database.

  • ttd_uid2_ids table has advertising_id. The value of advertising_id is UID2

Users incorporate the ID value into the audience data, then activate segmentation through the export integration with Trade Desk based on UID2 ID values.

The Trade Desk Export Integration

Appendix: Create the Workflow via Workflow

Instead of creating the UID2 workflow via API, the user can create the UID2 workflow by using the workflow.

Run a workflow

There are two options to define and run a workflow

  1. Use the graphical interface on TD Console > Data Workbench > Workflows
  2. Use the command-line interface of TD Toolbelt

While using the TD Console is more straightforward than the command-line interface, the TD CLI tool offers options for more complex configurations.

Refer here for more details about the syntax and usage of the TD Workflow

Workflow definition

_export:
  # Base configuration
  gateway_url: https://integrations-gateway.us01.treasuredata.com/integration_workflow/workflows/uid2/
  uid2_apikey: {uid2_apikey}
  uid2_secret: {uid2_secret}
  td_apikey: {td_apikey}
  host: {host}
  td_host: "api.treasuredata.com",

  
  # Database configuration
  database_name: {database_name}
  
  # Project configuration
  project_name: {project_name}
  revision: {project_revision}
  

+trigger_uid2_workflow:
  +prepare_request:
    echo>: "Preparing HTTP request to UID2 integration gateway"
    
  +execute_uid2_call:
    http>: ${gateway_url}
    method: POST
    headers:
      - Authorization: "TD1 ${td_apikey}"
      - Content-Type: "application/json"
    content: |
      {
        "type": "uid2",
        "plugin_config": {
          "type": "uid2",
          "host": "${host}",
          "apikey": "${uid2_apikey}",
          "secret": "${uid2_secret}",
          "database": "${database_name}",
          "td_apikey": "${td_apikey}",
          "td_host": "${td_host}",
          "uid2_limit_records": 2000000000,
          "enable_auto_format": true,
          "skip_limit": 500,
          "td_uid2_src_lst": [
            {
              "src_db": "source_database",
              "src_tbl": "email_table",
              "src_id_col": "129212",
              "src_dii_col": "userid",
              "src_dii_typ": "EMAIL"
            }
          ],
          "custom_batch_size": 5000,
          "custom_email_filter_import_uid2_identifier": "regexp_like(identifier, '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$')",
          "custom_phone_filter_import_uid2_identifier": "NOT regexp_like(identifier, '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$')"
        },
        "workflow_config": {
          "project_name": "${project_name}",
          "revision": "${revision}",
          "schedule": "daily>: 07:00:00",
          "timezone": "America/Los_Angeles"
        }
      }
    retry: 3
    store_content: true

  +echo_result:
    if>: ${http.last_content != null}
    _do:
      echo>: "Response details: ${http.last_content}"
    
+error_handling:
  _error:
    echo>: "Error triggering UID2 workflow. Check logs for details."

Parameters Reference

Workflow config

NameDescriptionValueDefault ValueRequired
project_nameProject name for workflow Please use a different project_name for each UID2 setupN/AN/AYes
revisionProject revision Using a different revision may override the existing project if there is a project with the same nameN/AN/AYes
scheduleSchedule for workflow Should be daily with time You can find support values and detailed instructions in Treasure Data’s documentation here: 🔗 Scheduling Workflows – Treasure Data DocsN/AN/AYes
timezoneThe timezone that the schedule should be set up inN/AUTCNo

Plugin config

NameDescriptionValueDefault ValueRequired
typeThe plugin typeuid2N/AYes
hostThe uid2 host Get from https://unifiedid.com/docs/getting-started/gs-environmentsN/AN/AYes
apikeyThe UID2 API keyN/AN/AYes
secretThe UID2 API client secretN/AN/AYes
databaseYour database name in TDN/AN/AYes
td_apikeyYour TD API keyN/AN/AYes
td_hostTD API hostN/AN/AYes
mapping_uid2_bulkload_session_nameYour previous bulkload mapping session. Use when you want to migrate to a new workflow but keep the bulkload session for incremental loadN/AN/ANo
td_uid2_src_lstList of DII table, imported for mapping UID2N/AN/AYes
skip_limitThe maximum number of invalid DII that  we can ignore  and process furtherN/A500No
uid2_limit_recordsThe maximum number of DII that are sent to map UID2N/A2.000.000.000No
custom_batch_sizeThe number of DII groups in one requestN/A10000No
enable_auto_formatEnable this option to auto-hash the DII email valueN/AtrueNo
custom_email_filter_import_uid2_identifierCustom email conditions to import into ttd_uid2_idsN/Aregexp_like(identifier, '''^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$''')No
custom_phone_filter_import_uid2_identifierCustom phone conditions to import into ttd_uid2_idsN/ANOT regexp_like(identifier, '''^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$''')No
custom_email_hash_filter_import_uid2_identifierCustom email_hash conditions to import into ttd_uid2_idsN/Aregexp_like(identifier, '^[a-zA-Z0-9+/]+={0,2}$')No
custom_phone_hash_filter_import_uid2_identifierCustom phone_hash conditions to import into ttd_uid2_idsN/Aregexp_like(identifier, '^[a-zA-Z0-9+/]+={0,2}$')No
last_uid2_request_timeStore the last UID2 request time for the next run. That value is used to select records in the ttd_uid2_rqst tableN/AN/ANo
last_uid2_request_indexStore the last UID2 request index for the next run. That value is used to select records in the ttd_uid2_rqst tableN/AN/ANo

Dii source list

NameDescriptionValueDefault ValueRequired
src_dbDII source databaseN/AN/AYes
src_tblDII source tableN/AN/AYes
src_id_col ID Column if available If no ID column, use the literal term "null" (w/o quotes)​N/AN/AYes
src_dii_colThe name of the Source DII column can be any nameN/AN/AYes
src_dii_typ The type of DII - EMAIL - PHONE - EMAIL_HASH - PHONE_HASH N/AN/AYes

About EUID

European Unified ID

EUID is an open-source, standalone solution with its own unique namespace that builds on the UID2 framework. The main differences between UID2 and EUID result from more stringent European and UK data protection laws relating to consent practices, rights for data subjects, and obligations between participants. Otherwise, EUID follows the same guiding principles as UID2.

Reference: https://euid.eu/docs/intro

Comparison AspectUID2EUID
Open-sourced frameworkYesYes
InteroperabilityYesYes
Personal data usedEmail addresses, phone numbersEmail addresses, phone numbers
ConsentBased on local regulations such as the California Privacy Rights Act (CPRA) and the California Consumer Privacy Act (CCPA).Driven by the General Data Protection Regulation (GDPR), the Transparency and Consent Framework (TCF) operated by IAB, and local regulatory guidance.

All EUID, use a different base URL: https://euid.eu/docs/getting-started/gs-environments