Skip to content

Integrate UI d2 python sdk into ttd data#24

Merged
adithyasamavedhi-ttd merged 4 commits into
mainfrom
Integrate-UID2-Python-SDK-into-ttd-data
May 19, 2026
Merged

Integrate UI d2 python sdk into ttd data#24
adithyasamavedhi-ttd merged 4 commits into
mainfrom
Integrate-UID2-Python-SDK-into-ttd-data

Conversation

@adithyasamavedhi-ttd
Copy link
Copy Markdown
Collaborator

@adithyasamavedhi-ttd adithyasamavedhi-ttd commented May 12, 2026

UID2 integration into ttd-data Python SDK

Adds ttd_data.DataClient — the recommended top-level client for the
ttd-data SDK. It enables ingesting advertiser, third-party, offline-conversion,
and deletion-opt-out data to the Trade Desk Data API endpoints, and — when
a UID2Config is supplied — resolves raw PII (email / phone /
hashed_email / hashed_phone) into UID2 (or EUID) tokens via the UID2
IdentityMapV3Client SDK before the request leaves the process, then
forwards a UID2-only payload to the Trade Desk Data APIs.

The Speakeasy-generated client is treated as an internal building block.
It is not re-exported from the package root and callers should not use
it directly.

Files

File Purpose (one line)
src/ttd_data/client.py DataClient wrapper around BaseDataClient that wires the resolver into each endpoint via sub-SDK proxies and merges UID2 failures back onto failed_lines.
src/ttd_data/uid2/config.py UID2Config dataclass + IdentityScope enum (UID2/EUID) holding base_url, api_key, client_secret.
src/ttd_data/uid2/models.py Subclassed data items (AdvertiserDataItem, ThirdPartyDataItem, OfflineConversionDataItem, PartnerDsrDataItem) that add raw-PII fields, plus response wrappers that attach identity_resolutions.
src/ttd_data/uid2/resolver.py The pre-request resolver: calls UID2 identity-map, mutates items in place (raw fields → UID2/EUID), and returns per-item resolutions + failed mappings.

ttd_data.DataClient, UID2Config, and IdentityScope are re-exported
from the package root. UID2-specific item / response classes live under
ttd_data.uid2.

Where to start reviewing

  1. src/ttd_data/client.pyDataClient class docstring + the
    sub-SDK proxies show the request lifecycle (resolve → convert →
    request → merge → wrap).
  2. src/ttd_data/uid2/resolver.py — module docstring covers the
    resolution rules, sentinel "*" behavior, and retry/abort policy.
  3. src/ttd_data/uid2/models.py — see how subclass items extend the
    Speakeasy base models with raw-PII fields and a model_validator
    that enforces "at most one UID2-family identifier per item".
  4. src/ttd_data/uid2/config.py — small, read this last.
  5. Smoke-test entrypoint: data-api-local/test_uid2_ingest.py exercises
    all six wrapped endpoints end-to-end.

Flow / assumptions

  • DataClient(uid2_config=None) is a thin pass-through over
    BaseDataClient — no UID2 work, same wire payload. Resolution only
    runs when a UID2Config is supplied.
  • UID2 resolution runs at the Python boundary, before Speakeasy
    validates or serializes, so subclass-only raw fields aren't stripped.
  • After resolution, raw PII fields are cleared on the item and only the
    resolved UID2/EUID (or TDID) is sent on the wire.
  • Per-item unmapped identifiers → sentinel "*" substituted so line
    numbers stay 1:1; the Trade Desk Data APIs reject "*", and the SDK
    merges those rejections back into failed_lines with
    ErrorCode = "Uid2Error".
  • Transient UID2 errors are retried (5x over 15s); catastrophic errors
    raise UID2ServiceError and abort the request entirely.
  • The UID2 SDK itself normalizes raw emails (lowercase, gmail-specific
    . / + handling) and validates raw phones (E.164) before hashing —
    what travels to the operator is always SHA-256+base64 hashes, never
    plaintext.

Glossary of the key classes

  • DataClient — top-level client for the ttd-data SDK; wraps
    BaseDataClient (the Speakeasy SDK) and, when uid2_config is
    supplied, adds UID2 resolution on the six PII-bearing endpoints.
  • BaseDataClient — the raw Speakeasy-generated client; internal
    building block, not exported from the package root.
  • UID2Config — credentials + scope (UID2 vs EUID) for the UID2 SDK.
  • Sub-SDK proxy (_AdvertiserProxy, _ThirdPartyProxy, etc.) —
    thin per-area proxy exposed as client.advertiser, client.third_party,
    ... that runs resolve → convert → request → merge → wrap and falls
    through to the underlying Speakeasy sub-SDK for any unwrapped method.
  • Subclassed data item (AdvertiserDataItem, ThirdPartyDataItem,
    OfflineConversionDataItem, PartnerDsrDataItem) — extends the
    Speakeasy base item with raw PII fields (email, phone,
    hashed_email, hashed_phone) that the resolver consumes; converted
    to the base item before the HTTP request is built.
  • Response wrapper (IngestAdvertiserDataResponse, etc.) — wraps the
    Speakeasy response and attaches identity_resolutions so callers can
    inspect each raw id's resolved UID2 / unmapped reason.

Endpoints wrapped

  • client.advertiser.ingest_advertiser_data
  • client.third_party.ingest_third_party_data
  • client.offline_conversion.ingest_offline_conversion_data
  • client.deletion_opt_out.data_subject_request_advertiser_data
  • client.deletion_opt_out.data_subject_request_merchant_data
  • client.deletion_opt_out.data_subject_request_third_party_data

Each has a sync and _async variant. Any other Speakeasy method falls
through unchanged via __getattr__.

Tests:

  1. advertiser
Screenshot 2026-05-14 at 1 24 41 PM
  1. thirdparty
Screenshot 2026-05-14 at 1 25 04 PM
  1. offline conversion
Screenshot 2026-05-14 at 1 38 14 PM
  1. deletion-optout advertiser
Screenshot 2026-05-14 at 1 24 07 PM
  1. deletion-optout thirdparty
Screenshot 2026-05-14 at 12 07 53 PM
  1. deletion-optout merchant
    Screenshot 2026-05-14 at 1 34 29 PM\

  2. Test UserIdType Enum for offline conversions

Screenshot 2026-05-15 at 5 18 41 PM
  1. Ensure that PII is dropped on the request to dataserver:
Screenshot 2026-05-18 at 3 34 35 PM

Check if its a breaking change for clients:

  1. I created this script with some test entried of how clients can send data to various TTD Data APIs:
    local_data_api_ingest_e2e.py

  2. I run the script on the current pypi version:

Screenshot 2026-05-15 at 3 02 49 PM Screenshot 2026-05-15 at 3 03 21 PM
  1. I run the script on this pr's sdk version:
Screenshot 2026-05-15 at 3 03 58 PM Screenshot 2026-05-15 at 3 04 30 PM

@adithyasamavedhi-ttd adithyasamavedhi-ttd requested a review from a team May 12, 2026 17:49
@adithyasamavedhi-ttd adithyasamavedhi-ttd force-pushed the Integrate-UID2-Python-SDK-into-ttd-data branch 11 times, most recently from 13dc2e0 to 7a51c53 Compare May 14, 2026 21:21
@adithyasamavedhi-ttd adithyasamavedhi-ttd force-pushed the Integrate-UID2-Python-SDK-into-ttd-data branch from 7a51c53 to c10c376 Compare May 14, 2026 21:40
Comment thread .speakeasy/gen.yaml Outdated
Comment thread data-api-local/.speakeasy/gen.yaml
Comment thread data-api-local/local_uid2_ingest_e2e.py Outdated
Comment thread src/ttd_data/client.py Outdated
Comment thread USAGE.md
Comment thread src/ttd_data/client.py Outdated
Comment thread tests/unit/test_uid2.py
Comment thread README.md
@adithyasamavedhi-ttd adithyasamavedhi-ttd force-pushed the Integrate-UID2-Python-SDK-into-ttd-data branch 8 times, most recently from 43a4b27 to aaa6aa3 Compare May 18, 2026 21:08
@adithyasamavedhi-ttd adithyasamavedhi-ttd force-pushed the Integrate-UID2-Python-SDK-into-ttd-data branch from aaa6aa3 to 27d8c27 Compare May 18, 2026 21:34
Comment thread README.md
@adithyasamavedhi-ttd adithyasamavedhi-ttd force-pushed the Integrate-UID2-Python-SDK-into-ttd-data branch 2 times, most recently from 72f6e5e to c724042 Compare May 19, 2026 17:36
@adithyasamavedhi-ttd adithyasamavedhi-ttd force-pushed the Integrate-UID2-Python-SDK-into-ttd-data branch from c724042 to 7a65a28 Compare May 19, 2026 17:57
@adithyasamavedhi-ttd adithyasamavedhi-ttd merged commit d411d10 into main May 19, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants