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.
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.
- Basic knowledge of Treasure Data
- Authorized Treasure Data account access
- Basic knowledge of UID2
- https://www.thetradedesk.com/us/about-us/industry-initiatives/unified-id-solution-2-0
- https://unifiedid.com/docs/intro or if you are in the EU: https://euid.eu/docs/intro
- Please refer to the About EUID section
| Term | Description |
|---|---|
| DII | DII is Directly Identifying Information, currently Email Addresses and Phone Numbers. |
| DII Normalization | DII (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 Rotation | UID2 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. |
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:
- In EU: https://euid.eu/docs/getting-started/gs-environments
- Others: https://unifiedid.com/docs/getting-started/gs-environments
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.
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.
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)
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
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 responsettd_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);- 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.
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": []
}Workflow config
| Name | Description | Value | Default Value | Required |
|---|---|---|---|---|
| project_name | Project name for workflow Please use a different project_name for each UID2 setup | N/A | N/A | Yes |
| revision | Project revision Using a different revision may override the existing project if there is a project with the same name | N/A | N/A | Yes |
| schedule | Schedule 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 Docs | N/A | N/A | Yes |
| timezone | The timezone that the schedule should be set up in | N/A | UTC | No |
| Name | Description | Value | Default Value | Required |
|---|---|---|---|---|
| type | The plugin type | uid2 | N/A | Yes |
| host | The uid2 host Get from https://unifiedid.com/docs/getting-started/gs-environments | N/A | N/A | Yes |
| apikey | The UID2 API key | N/A | N/A | Yes |
| secret | The UID2 API client secret | N/A | N/A | Yes |
| database | Your database name in TD | N/A | N/A | Yes |
| td_apikey | Your TD API key | N/A | N/A | Yes |
| td_host | TD API host | N/A | N/A | Yes |
| td_uid2_src_lst | List of DII table, imported for mapping UID2 | N/A | N/A | Yes |
| skip_limit | The maximum number of invalid DII that we can ignore and process further | N/A | 500 | No |
| uid2_limit_records | The maximum number of DII that are sent to map UID2 | N/A | 2.000.000.000 | No |
| custom_batch_size | The number of DII groups in one request | N/A | 10000 | No |
| enable_auto_format | Enable this option to auto-hash the DII email value | N/A | true | No |
| custom_email_filter_import_uid2_identifier | Format 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/A | regexp_like(identifier, '''^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$''') | No |
| custom_phone_filter_import_uid2_identifier | Format 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/A | NOT regexp_like(identifier, '''^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$''') | No |
| custom_email_hash_filter_import_uid2_identifier | Format 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 UID2 | N/A | regexp_like(identifier, '^[a-zA-Z0-9+/]+={0,2}$') | No |
| custom_phone_hash_filter_import_uid2_identifier | Format 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/A | regexp_like(identifier, '^[a-zA-Z0-9+/]+={0,2}$') | No |
| last_bucket_updated_time | Store the last bucket rotation time for the next run | N/A | 0 | No |
| last_uid2_request_time | Store the last UID2 request time for the next run. That value is used to select records in the ttd_uid2_rqst table | N/A | N/A | No |
| last_uid2_request_index | Store the last UID2 request index for the next run. That value is used to select records in the ttd_uid2_rqst table | N/A | N/A | No |
| Name | Description | Value | Default Value | Required |
|---|---|---|---|---|
| src_db | DII source database | N/A | N/A | Yes |
| src_tbl | DII source table | N/A | N/A | Yes |
| src_id_col | ID Column if available If no ID column, use the literal term "null" (w/o quotes) | N/A | N/A | Yes |
src_dii_col | The name of the Source DII column can be any name | N/A | N/A | Yes |
src_dii_typ | The type of DII - EMAIL - PHONE - EMAIL_HASH - PHONE_HASH | N/A | N/A | Yes |
Once created, you can access and monitor the workflow from the TD Console:
- Navigate to
- Data Workbench > Workflows.
- 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.


– Transactional Table – Main TTD UID2 Table
| COLUMN | TYPE | DESCRIPTION |
|---|---|---|
time | INTEGER | Unixtime of record INSERT |
src_db | VARCHAR | The source database of the DII value |
src_tbl | VARCHAR | The source table of the DII value |
src_id_col | VARCHAR | The ID column for the source table of the DII value |
src_id | VARCHAR | The ID value of the record in the source table of the DII value |
src_col | VARCHAR | The Source column in the source table of the DII value |
src_typ | VARCHAR | The type of DII, one of {EMAIL, PHONE} |
src_data | VARCHAR | The source DII value |
advertising_id | VARCHAR | The UID2 value (Defined as advertising_id in Service Operator Service API's) |
bucket_id | VARCHAR | The TTD Salt Bucket ID |
is_current | INTEGER | Does 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. |
Same schema as ttd_uid2_ids table, except that the is_current will always have the value -1 to indicate archive records.
| COLUMN | TYPE | DESCRIPTION |
|---|---|---|
time | INTEGER | Unixtime of record INSERT |
src_db | VARCHAR | The source database of the DII value |
src_tbl | VARCHAR | The source table of the DII value |
src_id_col | VARCHAR | The ID column for the source table of the DII value |
src_id | VARCHAR | The ID value of the record in the source table of the DII value |
src_col | VARCHAR | The Source column in the source table of the DII value |
src_typ | VARCHAR | The type of DII, one of {EMAIL, PHONE} |
src_data | VARCHAR | The source DII value |
advertising_id | VARCHAR | The TTD UID2 value (Defined as advertising_id in Operator Service API's) |
bucket_id | VARCHAR | The Salt Bucket ID |
is_current | INTEGER | Always has the value -1 (negative-one) to indicate archived records. |
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.
| COLUMN | TYPE | DESCRIPTION |
|---|---|---|
time | INTEGER | Unixtime of record INSERT |
bucket_id | VARCHAR | The UID Salt Bucket ID |
last_updated | VARCHAR | Timestamp in ISO 8601 format of when this Salt Bucket was last updated by the Operator (not used by this WF, for analysis purposes). |
| COLUMN | TYPE | DESCRIPTION |
|---|---|---|
time | INTEGER | Unixtime of record INSERT |
rnk_num | LONG | The sequence number of this UID2 Service Operator API batch request |
ttd_uid2_rqst | VARCHAR | The 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. |
| COLUMN | TYPE | DESCRIPTION |
|---|---|---|
time | INTEGER | Unixtime of record INSERT |
rnk_num | LONG | The sequence number of this UID2 Service Operator API batch request (not used by this WF, for analysis purposes). |
identifier | VARCHAR | The DII value, either an Email or Phone |
advertising_id | VARCHAR | The UID2 value (Defined as advertising_id in Operator Service API's) |
bucket_id | VARCHAR | The Salt Bucket ID |
| unmapped_reason | VARCHAR | The unmap reason for UID2 mapping |
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
Instead of creating the UID2 workflow via API, the user can create the UID2 workflow by using the workflow.
There are two options to define and run a workflow
- Use the graphical interface on TD Console > Data Workbench > Workflows
- 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
_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."| Name | Description | Value | Default Value | Required |
|---|---|---|---|---|
| project_name | Project name for workflow Please use a different project_name for each UID2 setup | N/A | N/A | Yes |
| revision | Project revision Using a different revision may override the existing project if there is a project with the same name | N/A | N/A | Yes |
| schedule | Schedule 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 Docs | N/A | N/A | Yes |
| timezone | The timezone that the schedule should be set up in | N/A | UTC | No |
| Name | Description | Value | Default Value | Required |
|---|---|---|---|---|
| type | The plugin type | uid2 | N/A | Yes |
| host | The uid2 host Get from https://unifiedid.com/docs/getting-started/gs-environments | N/A | N/A | Yes |
| apikey | The UID2 API key | N/A | N/A | Yes |
| secret | The UID2 API client secret | N/A | N/A | Yes |
| database | Your database name in TD | N/A | N/A | Yes |
| td_apikey | Your TD API key | N/A | N/A | Yes |
| td_host | TD API host | N/A | N/A | Yes |
| td_uid2_src_lst | List of DII table, imported for mapping UID2 | N/A | N/A | Yes |
| skip_limit | The maximum number of invalid DII that we can ignore and process further | N/A | 500 | No |
| uid2_limit_records | The maximum number of DII that are sent to map UID2 | N/A | 2.000.000.000 | No |
| custom_batch_size | The number of DII groups in one request | N/A | 10000 | No |
| enable_auto_format | Enable this option to auto-hash the DII email value | N/A | true | No |
| custom_email_filter_import_uid2_identifier | Custom email conditions to import into ttd_uid2_ids | N/A | regexp_like(identifier, '''^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$''') | No |
| custom_phone_filter_import_uid2_identifier | Custom phone conditions to import into ttd_uid2_ids | N/A | NOT regexp_like(identifier, '''^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$''') | No |
| custom_email_hash_filter_import_uid2_identifie | Custom email_hash conditions to import into ttd_uid2_ids | N/A | regexp_like(identifier, '^[a-zA-Z0-9+/]+={0,2}$') | No |
| custom_phone_hash_filter_import_uid2_identifie | Custom phone_hash conditions to import into ttd_uid2_ids | N/A | regexp_like(identifier, '^[a-zA-Z0-9+/]+={0,2}$') | No |
| last_bucket_updated_time | Store the last bucket rotation time for the next run | N/A | 0 | No |
| last_uid2_request_time | Store the last UID2 request time for the next run. That value is used to select records in the ttd_uid2_rqst table | N/A | N/A | No |
| last_uid2_request_index | Store the last UID2 request index for the next run. That value is used to select records in the ttd_uid2_rqst table | N/A | N/A | No |
| Name | Description | Value | Default Value | Required |
|---|---|---|---|---|
| src_db | DII source database | N/A | N/A | Yes |
| src_tbl | DII source table | N/A | N/A | Yes |
| src_id_col | ID Column if available If no ID column, use the literal term "null" (w/o quotes) | N/A | N/A | Yes |
src_dii_col | The name of the Source DII column can be any name | N/A | N/A | Yes |
src_dii_typ | The type of DII - EMAIL - PHONE - EMAIL_HASH - PHONE_HASH | N/A | N/A | Yes |
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 Aspect | UID2 | EUID |
|---|---|---|
| Open-sourced framework | Yes | Yes |
| Interoperability | Yes | Yes |
| Personal data used | Email addresses, phone numbers | Email addresses, phone numbers |
| Consent | Based 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