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.
Salt Bucket RotationUID2 values are kept encrypted within UID2 Service Operator systems, and the Salt values are rotated on average once per year on a fairly even basis. This means that roughly 1/365th of UID2 Salt Buckets will get rotated per day. In other words, if a customer has 80M DII ↔︎ UID2 mappings, then on average, 219,178 UID2 Salt Buckets will get rotated every day. When a Salt Bucket gets rotated, then the associated UID2 values are no longer valid and must be remapped.

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 also handles the critical task of "salt bucket rotation." Salt buckets are used to encrypt UID2s, and they are rotated periodically for security.
    • The workflow automatically identifies which UID2s have become stale due to rotation and remaps them.
  • 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_bucket_resp: a staging table that hold the JSON response of salt bucket response
  • 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);

CREATE TABLE IF NOT EXISTS "ttd_bucket_resp"("bucket_id" 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",
      "last_bucket_updated_time": "2025-04-28T07:02:00",
      "uid2_limit_records": 200000000,
      "enable_auto_format": true,
      "skip_limit": 500,
      "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
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_bucket_updated_timeStore the last bucket rotation time for the next runN/A0No
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 API's)
bucket_idVARCHARThe TTD Salt Bucket ID
is_currentINTEGERDoes the UID2 (advertising_id column) contain a current UID2 value from a non-expired Salt Bucket? - 0 (zero) – NO – Indicates that the ttd_uid2_ids record is either new or that the Salt Bucket has expired. In either case, a new UID2 must be fetched from TTD - 1 (one) – YES – Indicates that the ttd_uid2_ids The record has a current UID2 in the advertising_id column, a new UID2 does NOT need to be fetched from TTD The The is_current state is managed during each 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 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)
bucket_idVARCHARThe Salt Bucket ID
is_currentINTEGERAlways has the value -1 (negative-one) to indicate archived records.

ttd_bucket_resp – Staging Table – For UID Salt Bucket Rotation API Responses

This table is also used to calculate since_timestamp the Salt Bucket rotation API. Even though this table is classified as a staging table, the records should NEVER be manually deleted, as they are required for the subsequent WF run to accurately calculate the since_timestamp.

If the records in this table are ever accidentally deleted, then it is recommended to re-map UID2 for ALL records in the ttd_uid2_ids table.

COLUMNTYPEDESCRIPTION
timeINTEGERUnixtime of record INSERT
bucket_idVARCHARThe UID Salt Bucket ID
last_updatedVARCHARTimestamp in ISO 8601 format of when this Salt Bucket was last updated by the Operator (not used by this WF, for analysis purposes).

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)
bucket_idVARCHARThe Salt Bucket ID
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.

https://docs.treasuredata.com/articles/#!int/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}",
          "last_bucket_updated_time": "2025-04-28T07:02:00",
          "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
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_identifieCustom 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_identifieCustom phone_hash conditions to import into ttd_uid2_idsN/Aregexp_like(identifier, '^[a-zA-Z0-9+/]+={0,2}$')No
last_bucket_updated_timeStore the last bucket rotation time for the next runN/A0No
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