diff --git a/notebooks/audience_e2e_advertiser_flow.ipynb b/notebooks/audience_e2e_advertiser_flow.ipynb
new file mode 100644
index 0000000..a751e5b
--- /dev/null
+++ b/notebooks/audience_e2e_advertiser_flow.ipynb
@@ -0,0 +1,532 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "cb97ec0d",
+ "metadata": {
+ "id": "h2sCjFoFUEYZ"
+ },
+ "source": [
+ "Project: /data-manager/api/_project.yaml\n",
+ "Book: /data-manager/api/_book.yaml\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5aa2bcd2",
+ "metadata": {
+ "cellView": "form",
+ "id": "ZL8ttX6NRUE0"
+ },
+ "outputs": [],
+ "source": [
+ "# @markdown #### Copyright 2026 Google LLC\n",
+ "# @markdown ##### Licensed under the Apache License, Version 2.0 (the \"License\");\n",
+ "# you may not use this file except in compliance with the License.\n",
+ "# You may obtain a copy of the License at\n",
+ "#\n",
+ "# https://www.apache.org/licenses/LICENSE-2.0\n",
+ "#\n",
+ "# Unless required by applicable law or agreed to in writing, software\n",
+ "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
+ "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
+ "# See the License for the specific language governing permissions and\n",
+ "# limitations under the License."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "399b973a",
+ "metadata": {},
+ "source": [
+ "# Advertiser Flow"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a7eda861",
+ "metadata": {},
+ "source": [
+ "## Objective\n",
+ "This notebook provides the workflow for an Advertiser operating on their own Google Ads account.\n",
+ "It can authenticate using either OAuth Client Credentials or a Service Account.\n",
+ "\n",
+ "## Prerequisites\n",
+ "\n",
+ "One of the following:\n",
+ "\n",
+ "- A client ID, client secret, and refresh token for a Google Account with access to your Google Ads account.\n",
+ "\n",
+ " Use this option if you've already gone through the process of generating user credentials.\n",
+ "\n",
+ "- The JSON for a **Desktop app** OAuth client.\n",
+ "\n",
+ " [Follow these instructions](https://developers.google.com/data-manager/api/devguides/quickstart/set-up-access#user-account) if you don't have this JSON file. You'll use the JSON file you download for the prompt in **Step 1**.\n",
+ "\n",
+ "- The email address of a [service account](//cloud.google.com/docs/authentication#service-accounts).\n",
+ "\n",
+ " [Follow these instructions](https://developers.google.com/data-manager/api/devguides/quickstart/set-up-access#service-account) if you don't have a service account.\n",
+ "\n",
+ "\n",
+ "## Instructions\n",
+ "1. Make a copy of this Colab.\n",
+ "2. Fill in the prompts in **Step 1**.\n",
+ "3. Run your copy of the Colab.\n"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "0cd51598",
+ "metadata": {
+ "id": "-wtv73t1egvl"
+ },
+ "source": [
+ "### Step 0. Install and Import Packages"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b0c3f5c5",
+ "metadata": {
+ "cellView": "form",
+ "collapsed": true,
+ "id": "j9kMcKunX3dd"
+ },
+ "outputs": [],
+ "source": [
+ "!pip install --upgrade google-ads-datamanager google-auth-oauthlib"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "89bba485",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import datetime\n",
+ "import getpass\n",
+ "from google.ads import datamanager_v1\n",
+ "import google.auth\n",
+ "from google.colab import files\n",
+ "from google.oauth2.credentials import Credentials\n",
+ "from google.protobuf.json_format import MessageToJson"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b5345bff",
+ "metadata": {
+ "id": "81MXNTkJezi9"
+ },
+ "source": [
+ "### Step 1. Setup and Configuration"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6147f75d",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "authentication_method = \"OAuth Client Credentials\" # @param [\"OAuth Client Credentials\", \"Service Account\"]\n",
+ "\n",
+ "# @markdown *Check this if you already have a Client ID, Client Secret, and Refresh Token to bypass the gcloud setup:*\n",
+ "use_existing_credentials = False # @param {type:\"boolean\"}\n",
+ "\n",
+ "# @markdown ### Account IDs\n",
+ "# @markdown **ADVERTISERS:**\n",
+ "# @markdown - **[required]** `operating_account_id`: Your advertiser account\n",
+ "# @markdown - (*optional*) `login_account_id`: Don't set this if the Google Account of your credentials is a user in the advertiser account. If instead the Google Account has access to a Google Ads manager account with the advertiser account as a child, set this to the customer ID of the Google Ads manager account.\n",
+ "operating_account_id = \"\" # @param {type:\"string\"}\n",
+ "login_account_id = \"\" # @param {type:\"string\"}\n",
+ "\n",
+ "# @markdown ### Service Account Details (If applicable)\n",
+ "service_account_email = \"\" # @param {type:\"string\"}\n",
+ "\n",
+ "# Clean up account IDs by removing hyphens.\n",
+ "operating_account_id = operating_account_id.replace(\"-\", \"\")\n",
+ "login_account_id = login_account_id.replace(\"-\", \"\")\n",
+ "\n",
+ "GOOGLE_ADS = \"GOOGLE_ADS\"\n",
+ "CONSENT_GRANTED = \"CONSENT_GRANTED\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b20add50",
+ "metadata": {
+ "id": "Kv3wnXRDfBgY"
+ },
+ "source": [
+ "### Step 2. Initialize Client"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "f7c4a1ab",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class DataManagerSDK:\n",
+ " def __init__(self, creds):\n",
+ " self.user_list_service = datamanager_v1.UserListServiceClient(credentials=creds)\n",
+ " self.ingestion_service = datamanager_v1.IngestionServiceClient(credentials=creds)\n",
+ "\n",
+ "def prompt_for_desktop_client_json():\n",
+ "\n",
+ " # Prompts the user to upload the JSON file\n",
+ " print(\"Upload the client JSON file for your Google Cloud Desktop OAuth client:\")\n",
+ " uploaded = files.upload()\n",
+ " desktop_app_json_file = \"/tmp/oauth_client.json\"\n",
+ "\n",
+ " if len(uploaded) != 1:\n",
+ " raise ValueError(\"Please upload exactly one file.\")\n",
+ "\n",
+ " filename = list(uploaded.keys())[0]\n",
+ " content = uploaded[filename]\n",
+ "\n",
+ " # Saves the uploaded file.\n",
+ " with open(desktop_app_json_file, \"wb\") as f:\n",
+ " f.write(content)\n",
+ " # Returns the file path and name.\n",
+ " return desktop_app_json_file\n",
+ "\n",
+ "def initialize_client():\n",
+ " data_manager_scope = \"https://www.googleapis.com/auth/datamanager\";\n",
+ " creds = None\n",
+ "\n",
+ " if authentication_method == \"OAuth Client Credentials\":\n",
+ " if use_existing_credentials:\n",
+ " print(\"Please enter your OAuth credentials:\")\n",
+ " client_id = input(\"Client ID: \").strip()\n",
+ " client_secret = getpass.getpass(\"Client Secret (input will be hidden): \").strip()\n",
+ " refresh_token = getpass.getpass(\"Refresh Token (input will be hidden): \").strip()\n",
+ "\n",
+ " creds = Credentials(\n",
+ " token=None,\n",
+ " client_id=client_id,\n",
+ " client_secret=client_secret,\n",
+ " refresh_token=refresh_token,\n",
+ " token_uri=\"https://oauth2.googleapis.com/token\",\n",
+ " scopes=[data_manager_scope]\n",
+ " )\n",
+ " else:\n",
+ " print(\"Authenticating with OAuth via gcloud...\")\n",
+ " client_json_file = prompt_for_desktop_client_json()\n",
+ " auth_command = (\n",
+ " f\"gcloud auth application-default login \"\n",
+ " f\"--client-id-file='{client_json_file}' \"\n",
+ " f\"--scopes={data_manager_scope},https://www.googleapis.com/auth/cloud-platform \"\n",
+ " f\"--no-browser\"\n",
+ " )\n",
+ " print(\"Running gcloud auth command...\")\n",
+ " get_ipython().system(auth_command)\n",
+ " print(\"gcloud auth process finished\")\n",
+ " creds, project = google.auth.default(scopes=[data_manager_scope])\n",
+ " else:\n",
+ " print(\"Authenticating with Service Account...\")\n",
+ " auth_command = (\n",
+ " f\"gcloud auth application-default login \"\n",
+ " f\"--impersonate-service-account={service_account_email} \"\n",
+ " f\"--scopes={data_manager_scope},https://www.googleapis.com/auth/cloud-platform \"\n",
+ " f\"--no-browser\"\n",
+ " )\n",
+ " print(\"Running gcloud auth command...\")\n",
+ " get_ipython().system(auth_command)\n",
+ " print(\"gcloud auth process finished\")\n",
+ " creds, project = google.auth.default(scopes=[data_manager_scope])\n",
+ "\n",
+ " return DataManagerSDK(creds)\n",
+ "\n",
+ "sdk = initialize_client()"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d822024f",
+ "metadata": {
+ "id": "89jsXS8jfQSd"
+ },
+ "source": [
+ "### Step 3. Create User List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "4db10e1c",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(f\"Creating User List in {operating_account_id}...\\n\")\n",
+ "\n",
+ "parent_userlist = f\"accountTypes/GOOGLE_ADS/accounts/{operating_account_id}\"\n",
+ "\n",
+ "user_list_data = datamanager_v1.UserList(\n",
+ " display_name=f\"Python SDK Audience - {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\",\n",
+ " ingested_user_list_info=datamanager_v1.IngestedUserListInfo(\n",
+ " upload_key_types=[\"CONTACT_ID\"]\n",
+ " ),\n",
+ ")\n",
+ "\n",
+ "headers = []\n",
+ "if login_account_id:\n",
+ " headers.append(\n",
+ " (\n",
+ " \"login-account\",\n",
+ " f\"accountTypes/GOOGLE_ADS/accounts/{login_account_id}\",\n",
+ " )\n",
+ " )\n",
+ "\n",
+ "destination_id = None\n",
+ "try:\n",
+ " ulist_res = sdk.user_list_service.create_user_list(\n",
+ " parent=parent_userlist, user_list=user_list_data, metadata=headers\n",
+ " )\n",
+ "\n",
+ " destination_id = ulist_res.id\n",
+ "\n",
+ " print(f\"User List Created!\")\n",
+ " print(f\"Destination ID: {destination_id}\")\n",
+ " print(f\"Resource Name: {ulist_res.name}\")\n",
+ " print(f\"Display Name: {ulist_res.display_name}\")\n",
+ "\n",
+ "except Exception as e:\n",
+ " print(f\"Failed to create User List: {e}\")\n",
+ "\n",
+ "print(\n",
+ " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/accountTypes.accounts.userLists/create?apix=true\"\n",
+ ")\n",
+ "print(f\"\\nparent: {parent_userlist}\")\n",
+ "\n",
+ "print(\"\\n--- Request JSON Payload ---\")\n",
+ "print(MessageToJson(user_list_data._pb))\n",
+ "print(\"----------------------------\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "1d6e78c7",
+ "metadata": {
+ "id": "CrmA_yg0fUad"
+ },
+ "source": [
+ "### Step 4. Ingest Data"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8dc8934f",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ingestion_request_id = None\n",
+ "\n",
+ "if not destination_id:\n",
+ " print(\n",
+ " \"Error: destination_id is not set. Cannot ingest data. Please ensure the User List was created successfully in the previous step.\"\n",
+ " )\n",
+ "else:\n",
+ " print(f\"Ingesting sample data to User List {destination_id}...\")\n",
+ "\n",
+ " destination_obj = datamanager_v1.Destination(\n",
+ " operating_account=datamanager_v1.ProductAccount(\n",
+ " account_type=GOOGLE_ADS, account_id=operating_account_id\n",
+ " ),\n",
+ " product_destination_id=str(destination_id),\n",
+ " )\n",
+ "\n",
+ " if login_account_id:\n",
+ " destination_obj.login_account = datamanager_v1.ProductAccount(\n",
+ " account_type=GOOGLE_ADS, account_id=login_account_id\n",
+ " )\n",
+ "\n",
+ " ingest_payload = datamanager_v1.IngestAudienceMembersRequest(\n",
+ " consent=datamanager_v1.Consent(\n",
+ " ad_user_data=CONSENT_GRANTED, ad_personalization=CONSENT_GRANTED\n",
+ " ),\n",
+ " encoding=datamanager_v1.Encoding.HEX,\n",
+ " terms_of_service=datamanager_v1.TermsOfService(\n",
+ " customer_match_terms_of_service_status=\"ACCEPTED\"\n",
+ " ),\n",
+ " validate_only=False,\n",
+ " audience_members=[\n",
+ " datamanager_v1.AudienceMember(\n",
+ " user_data=datamanager_v1.UserData(\n",
+ " user_identifiers=[\n",
+ " datamanager_v1.UserIdentifier(\n",
+ " email_address=\"223EBDA6F6889B1494551BA902D9D381DAF2F642BAE055888E96343D53E9F9C4\"\n",
+ " ),\n",
+ " datamanager_v1.UserIdentifier(\n",
+ " email_address=\"F1FCDE379F31F4D446B76EE8F34860ECA2288ADC6B6D6C0FDC56D9EEE75A2FA5\"\n",
+ " ),\n",
+ " ]\n",
+ " )\n",
+ " )\n",
+ " ],\n",
+ " destinations=[destination_obj],\n",
+ " )\n",
+ "\n",
+ " try:\n",
+ " ingest_res = sdk.ingestion_service.ingest_audience_members(\n",
+ " request=ingest_payload\n",
+ " )\n",
+ "\n",
+ " ingestion_request_id = ingest_res.request_id\n",
+ " print(f\"Ingestion Submitted!\")\n",
+ " print(f\"Request ID: {ingestion_request_id}\")\n",
+ "\n",
+ " except Exception as e:\n",
+ " print(f\"Failed to ingest data: {e}\")\n",
+ "\n",
+ " print(\n",
+ " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/audienceMembers/ingest?apix=true\"\n",
+ " )\n",
+ " print(\"\\n--- Request JSON Payload ---\")\n",
+ " print(MessageToJson(ingest_payload._pb))\n",
+ " print(\"----------------------------\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "68eab82f",
+ "metadata": {
+ "id": "DcXOh5xJfYdh"
+ },
+ "source": [
+ "### Step 5. Check Status"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a05f95c3",
+ "metadata": {
+ "id": "DiEenmMYiIN3"
+ },
+ "outputs": [],
+ "source": [
+ "if not ingestion_request_id:\n",
+ " print(\"Status check skipped because ingestion request ID is not set.\")\n",
+ "else:\n",
+ " print(f\"5. Checking status for Request ID: {ingestion_request_id}...\")\n",
+ "\n",
+ " # Note: Headers aren't needed for this request.\n",
+ " status_req = datamanager_v1.RetrieveRequestStatusRequest(\n",
+ " request_id=ingestion_request_id\n",
+ " )\n",
+ "\n",
+ " try:\n",
+ " status_res = sdk.ingestion_service.retrieve_request_status(\n",
+ " request=status_req\n",
+ " )\n",
+ "\n",
+ " print(f\"Successfully retrieved status!\")\n",
+ "\n",
+ " for dest_status in status_res.request_status_per_destination:\n",
+ " dest_id = dest_status.destination.product_destination_id\n",
+ " current_status = dest_status.request_status.name\n",
+ "\n",
+ " print(f\"Status for destination {dest_id}: {current_status}\")\n",
+ "\n",
+ " except Exception as e:\n",
+ " print(f\"Failed to retrieve status: {e}\")\n",
+ "\n",
+ " print(\n",
+ " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/requestStatus/retrieve?apix=true\"\n",
+ " )\n",
+ " print(\"\\n--- Request JSON Payload ---\")\n",
+ " print(MessageToJson(status_req._pb))\n",
+ " print(\"----------------------------\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "colab": {
+ "private_outputs": true,
+ "provenance": []
+ },
+ "kernelspec": {
+ "display_name": "Python 3",
+ "name": "python3"
+ },
+ "language_info": {
+ "name": "python"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/notebooks/audience_e2e_data_partner_dual_persona.ipynb b/notebooks/audience_e2e_data_partner_dual_persona.ipynb
new file mode 100644
index 0000000..655e603
--- /dev/null
+++ b/notebooks/audience_e2e_data_partner_dual_persona.ipynb
@@ -0,0 +1,693 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "8c6a9fa0",
+ "metadata": {
+ "id": "h2sCjFoFUEYZ"
+ },
+ "source": [
+ "Project: /data-manager/api/_project.yaml\n",
+ "Book: /data-manager/api/_book.yaml\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "056ea28b",
+ "metadata": {
+ "cellView": "form",
+ "id": "ZL8ttX6NRUE0"
+ },
+ "outputs": [],
+ "source": [
+ "# @markdown #### Copyright 2026 Google LLC\n",
+ "# @markdown ##### Licensed under the Apache License, Version 2.0 (the \"License\");\n",
+ "# you may not use this file except in compliance with the License.\n",
+ "# You may obtain a copy of the License at\n",
+ "#\n",
+ "# https://www.apache.org/licenses/LICENSE-2.0\n",
+ "#\n",
+ "# Unless required by applicable law or agreed to in writing, software\n",
+ "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
+ "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
+ "# See the License for the specific language governing permissions and\n",
+ "# limitations under the License."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "ef431906",
+ "metadata": {},
+ "source": [
+ "# Data Partner: Dual Persona Workflow (In-Platform Experience)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "84041125",
+ "metadata": {},
+ "source": [
+ "## Objective\n",
+ "\n",
+ "This notebook simulates the exact flow required for App Verification, where advertisers link to a partner within your UI.\n",
+ "\n",
+ "## Prerequisites\n",
+ "\n",
+ "1. The JSON for a **Desktop app** OAuth client.\n",
+ "\n",
+ " [Follow these instructions](https://developers.google.com/data-manager/api/devguides/quickstart/set-up-access#user-account) if you don't have this JSON file. You'll use the JSON file you download for the prompt in **Phase 0**.\n",
+ "\n",
+ "2. The email address and project ID of a [service account](//cloud.google.com/docs/authentication#service-accounts).\n",
+ "\n",
+ " [Follow these instructions](https://developers.google.com/data-manager/api/devguides/quickstart/set-up-access#service-account) if you don't have a service account.\n",
+ "\n",
+ "## Phases\n",
+ "\n",
+ "* **Phase 0:** Provide OAuth Information and Account IDs\n",
+ "\n",
+ " Fill in the prompts with information about your service account, data partner, and advertiser account.\n",
+ "\n",
+ " Upload the JSON file for your OAuth Desktop app.\n",
+ "\n",
+ "* **Phase 1:** Act as the Advertiser\n",
+ "\n",
+ " Create a partner link from the advertiser to the data partner. This step uses user credentials for a user with access to the advertiser account.\n",
+ "\n",
+ "* **Phase 2:** Act as the Data Partner\n",
+ "\n",
+ " Retrieve the partner link, create an audience, push data, and check the processing status. This step uses service account credentials for a service account with access to the data partner account.\n",
+ "\n",
+ "## Instructions\n",
+ "\n",
+ "1. Make a copy of this Colab.\n",
+ "2. Fill in the prompts in **Phase 0**.\n",
+ "3. Run your copy of the Colab."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b901130d",
+ "metadata": {
+ "id": "-wtv73t1egvl"
+ },
+ "source": [
+ "### Step 0. Install and Import Packages"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "c9bb983c",
+ "metadata": {
+ "cellView": "form",
+ "collapsed": true,
+ "id": "j9kMcKunX3dd"
+ },
+ "outputs": [],
+ "source": [
+ "!pip install --upgrade google-ads-datamanager google-auth-oauthlib"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "72a39bf1",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import datetime\n",
+ "import getpass\n",
+ "from google.ads import datamanager_v1\n",
+ "import google.auth\n",
+ "from google.colab import files\n",
+ "from google.oauth2.credentials import Credentials\n",
+ "from google.protobuf.json_format import MessageToJson"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "59971378",
+ "metadata": {},
+ "source": [
+ "## Phase 0: Provide OAuth Information and Account IDs"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "706c6a04",
+ "metadata": {},
+ "source": [
+ "### Step 1: Provide credentials information and account IDs"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "917707e7",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# @markdown ### Account IDs\n",
+ "# @markdown Provide the advertiser and data partner account IDs below.\n",
+ "# @markdown - **[required]** `login_account_id`: Your Data Partner account.\n",
+ "# @markdown - **[required]** `operating_account_id`: The Google Ads advertiser account that will own the audience you create as part of this workflow.\n",
+ "# @markdown - (*optional*}) `linked_account_id`: If you want to link the `operating_account` directly to your data partner account, leave this blank. If instead you want to link a Google Ads manager account (where the `operating_account` is a child) to your data partner account, set this to the customer ID of the Google Ads manager account.\n",
+ "login_account_id = \"\" # @param {type:\"string\"}\n",
+ "operating_account_id = \"\" # @param {type:\"string\"}\n",
+ "linked_account_id = \"\" # @param {type:\"string\"}\n",
+ "\n",
+ "# @markdown *Check this if you already have a Client ID, Client Secret, and Refresh Token for the Advertiser to bypass the gcloud setup:*\n",
+ "use_existing_advertiser_credentials = False # @param {type:\"boolean\"}\n",
+ "\n",
+ "# @markdown ### Service Account Details (If applicable)\n",
+ "service_account_email = \"\" # @param {type:\"string\"}\n",
+ "\n",
+ "# Clean up account IDs by removing hyphens.\n",
+ "login_account_id = login_account_id.replace(\"-\", \"\")\n",
+ "operating_account_id = operating_account_id.replace(\"-\", \"\")\n",
+ "linked_account_id = linked_account_id.replace(\"-\", \"\")\n",
+ "\n",
+ "# Sets the linked_account_id to the operating account if linking directly to the operating account.\n",
+ "linked_account_id = linked_account_id or operating_account_id\n",
+ "\n",
+ "# Constants.\n",
+ "GOOGLE_ADS = \"GOOGLE_ADS\"\n",
+ "DATA_PARTNER = \"DATA_PARTNER\"\n",
+ "CONSENT_GRANTED = \"CONSENT_GRANTED\"\n",
+ "PARTNER_LINK_SCOPE = \"https://www.googleapis.com/auth/datamanager.partnerlink\"\n",
+ "DESKTOP_APP_JSON_FILE = \"/tmp/oauth_client.json\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d59c5021",
+ "metadata": {},
+ "source": [
+ "### Step 2: Upload OAuth Desktop client JSON for the Advertiser credentials\n",
+ "\n",
+ "**IMPORTANT:** Follow the prompts in the command output to complete this step."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b586c553",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "if use_existing_advertiser_credentials:\n",
+ " print(\n",
+ " \"Skipping OAuth client JSON upload since we're using preexisting Advertiser OAuth credentials.\"\n",
+ " )\n",
+ "else:\n",
+ " # Prompts the user to upload the JSON file\n",
+ " print(\n",
+ " \"Upload the client JSON file for your Google Cloud Desktop OAuth client:\"\n",
+ " )\n",
+ " uploaded = files.upload()\n",
+ "\n",
+ " if len(uploaded) != 1:\n",
+ " raise ValueError(\"Please upload exactly one file\")\n",
+ "\n",
+ " filename = list(uploaded.keys())[0]\n",
+ " content = uploaded[filename]\n",
+ "\n",
+ " # Saves the uploaded file.\n",
+ " with open(DESKTOP_APP_JSON_FILE, \"wb\") as f:\n",
+ " f.write(content)"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cc65d910",
+ "metadata": {},
+ "source": [
+ "## Phase 1: Advertiser Creates Partner Link"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "8bc0b07c",
+ "metadata": {},
+ "source": [
+ "### Step 1. Initialize Advertiser Client\n",
+ "\n",
+ "**IMPORTANT:** Follow the prompts in the command output to complete this step."
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "5b82d2d2",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class DataManagerAdvertiserSDK:\n",
+ " def __init__(self, creds):\n",
+ " self.link_service = datamanager_v1.PartnerLinkServiceClient(credentials=creds)\n",
+ "\n",
+ "def initialize_advertiser_client():\n",
+ " print(\"Authenticating Advertiser with OAuth...\")\n",
+ " creds = None\n",
+ "\n",
+ " try:\n",
+ " if use_existing_advertiser_credentials:\n",
+ " print(\"Using existing Advertiser OAuth credentials\")\n",
+ " print()\n",
+ " # Prompts the user for the client ID, client secret, and refresh token.\n",
+ " print(\"Please enter your preexisting Advertiser OAuth credentials:\")\n",
+ " advertiser_oauth_client_id = input(\"Client ID: \").strip()\n",
+ " advertiser_oauth_client_secret = getpass.getpass(\n",
+ " \"Client Secret (input will be hidden): \"\n",
+ " ).strip()\n",
+ " advertiser_oauth_client_refresh_token = getpass.getpass(\n",
+ " \"Refresh Token (input will be hidden): \"\n",
+ " ).strip()\n",
+ "\n",
+ " # Creates user credentials using the provided values.\n",
+ " creds = Credentials(\n",
+ " token=None,\n",
+ " client_id=advertiser_oauth_client_id,\n",
+ " client_secret=advertiser_oauth_client_secret,\n",
+ " refresh_token=advertiser_oauth_client_refresh_token,\n",
+ " token_uri=\"https://oauth2.googleapis.com/token\",\n",
+ " scopes=[PARTNER_LINK_SCOPE]\n",
+ " )\n",
+ " else:\n",
+ " # Run the gcloud command\n",
+ " # Uses the less permissive datamanager.partnerlink scope since the\n",
+ " # only action the advertiser needs to perform is creating the\n",
+ " # partner link.\n",
+ " print(\"Running gcloud auth to get Advertiser credentials...\")\n",
+ " auth_command = (\n",
+ " f\"gcloud auth application-default login \"\n",
+ " f\"--client-id-file='{DESKTOP_APP_JSON_FILE}' \"\n",
+ " f\"--scopes={PARTNER_LINK_SCOPE},https://www.googleapis.com/auth/cloud-platform \"\n",
+ " f\"--no-browser\"\n",
+ " )\n",
+ " get_ipython().system(auth_command)\n",
+ "\n",
+ " print()\n",
+ " print(\"gcloud auth process finished.\")\n",
+ "\n",
+ " creds, project = google.auth.default(scopes=[PARTNER_LINK_SCOPE])\n",
+ "\n",
+ " except Exception as e:\n",
+ " print(f\"An error occurred: {e}\")\n",
+ "\n",
+ " return DataManagerAdvertiserSDK(creds)\n",
+ "\n",
+ "advertiser_sdk = initialize_advertiser_client()\n",
+ "print(\"Advertiser SDK Initialized\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "7ccb2114",
+ "metadata": {
+ "id": "--2LPM7_fKZM"
+ },
+ "source": [
+ "### Step 2. Create Partner Link"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "aa1cf9d3",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(f\"Creating link for Ads {linked_account_id}..\\n\")\n",
+ "\n",
+ "parent_ads = f\"accountTypes/GOOGLE_ADS/accounts/{linked_account_id}\"\n",
+ "\n",
+ "partner_link_data = datamanager_v1.PartnerLink(\n",
+ " owning_account=datamanager_v1.ProductAccount(\n",
+ " account_id=linked_account_id, account_type=GOOGLE_ADS\n",
+ " ),\n",
+ " partner_account=datamanager_v1.ProductAccount(\n",
+ " account_id=login_account_id, account_type=DATA_PARTNER\n",
+ " ),\n",
+ ")\n",
+ "\n",
+ "try:\n",
+ " link_res = advertiser_sdk.link_service.create_partner_link(\n",
+ " parent=parent_ads, partner_link=partner_link_data\n",
+ " )\n",
+ " print(f\"Link Created: {link_res.partner_link_id}\")\n",
+ " product_link_id = link_res.partner_link_id\n",
+ "except Exception as e:\n",
+ " print(f\"Link creation failed or exists: {e}\")\n",
+ " print(\"\\nSearching for existing link...\")\n",
+ " search_res = advertiser_sdk.link_service.search_partner_links(\n",
+ " parent=f\"accountTypes/DATA_PARTNER/accounts/{login_account_id}\"\n",
+ " )\n",
+ " product_link_id = None\n",
+ " for link in search_res:\n",
+ " if link.owning_account.account_id == linked_account_id:\n",
+ " product_link_id = link.partner_link_id\n",
+ " print(f\"Found existing Link ID: {product_link_id}\")\n",
+ " break\n",
+ "\n",
+ "print(\n",
+ " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/accountTypes.accounts.partnerLinks/create?apix=true\"\n",
+ ")\n",
+ "print(f\"\\nparent: {parent_ads}\")\n",
+ "print(\"\\n--- Request JSON Payload ---\")\n",
+ "print(MessageToJson(partner_link_data._pb))\n",
+ "print(\"----------------------------\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "cfc3a10b",
+ "metadata": {},
+ "source": [
+ "## Phase 2: Data Partner Ingests Data"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5c94c532",
+ "metadata": {},
+ "source": [
+ "### Step 1. Initialize Data Partner Client"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a54be3c6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class DataManagerPartnerSDK:\n",
+ " def __init__(self, creds):\n",
+ " self.user_list_service = datamanager_v1.UserListServiceClient(credentials=creds)\n",
+ " self.ingestion_service = datamanager_v1.IngestionServiceClient(credentials=creds)\n",
+ "\n",
+ "def initialize_partner_client():\n",
+ " print(\"Authenticating Data Partner with Service Account...\")\n",
+ " data_manager_scope = \"https://www.googleapis.com/auth/datamanager\"\n",
+ " auth_command = (\n",
+ " f\"gcloud auth application-default login \"\n",
+ " f\"--impersonate-service-account={service_account_email} \"\n",
+ " f\"--scopes={data_manager_scope},https://www.googleapis.com/auth/cloud-platform \"\n",
+ " f\"--no-browser\"\n",
+ " )\n",
+ " get_ipython().system(auth_command)\n",
+ " creds, project = google.auth.default(scopes=[data_manager_scope])\n",
+ " return DataManagerPartnerSDK(creds)\n",
+ "\n",
+ "partner_sdk = initialize_partner_client()\n",
+ "print(\"Data Partner SDK Initialized\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "494a47fa",
+ "metadata": {
+ "id": "89jsXS8jfQSd"
+ },
+ "source": [
+ "### Step 2. Create User List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1b41b5e0",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "print(f\"Creating User List in {operating_account_id}...\\n\")\n",
+ "\n",
+ "parent_userlist = f\"accountTypes/GOOGLE_ADS/accounts/{operating_account_id}\"\n",
+ "\n",
+ "user_list_data = datamanager_v1.UserList(\n",
+ " display_name=f\"Python SDK Audience - {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\",\n",
+ " ingested_user_list_info=datamanager_v1.IngestedUserListInfo(\n",
+ " upload_key_types=[\"CONTACT_ID\"]\n",
+ " ),\n",
+ ")\n",
+ "\n",
+ "login_account = f\"accountTypes/DATA_PARTNER/accounts/{login_account_id}\"\n",
+ "linked_account = f\"accountTypes/GOOGLE_ADS/accounts/{linked_account_id}\"\n",
+ "\n",
+ "# Creates a list of header tuples.\n",
+ "headers = [\n",
+ " (\"login-account\", login_account),\n",
+ " (\"linked-account\", linked_account),\n",
+ "]\n",
+ "\n",
+ "destination_id = None\n",
+ "try:\n",
+ " ulist_res = partner_sdk.user_list_service.create_user_list(\n",
+ " parent=parent_userlist, user_list=user_list_data, metadata=headers\n",
+ " )\n",
+ "\n",
+ " destination_id = ulist_res.id\n",
+ "\n",
+ " print(f\"User List Created!\")\n",
+ " print(f\"Destination ID: {destination_id}\")\n",
+ " print(f\"Resource Name: {ulist_res.name}\")\n",
+ " print(f\"Display Name: {ulist_res.display_name}\")\n",
+ "\n",
+ "except Exception as e:\n",
+ " print(f\"Failed to create User List: {e}\")\n",
+ "\n",
+ "print(\n",
+ " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/accountTypes.accounts.userLists/create?apix=true\"\n",
+ ")\n",
+ "print(f\"\\nparent: {parent_userlist}\")\n",
+ "print(f\"login-account: {login_account}\")\n",
+ "print(f\"linked-account: {linked_account}\")\n",
+ "print(\"\\n--- Request JSON Payload ---\")\n",
+ "print(MessageToJson(user_list_data._pb))\n",
+ "print(\"----------------------------\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "3ab62226",
+ "metadata": {
+ "id": "CrmA_yg0fUad"
+ },
+ "source": [
+ "### Step 3. Ingest Data"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "9ec8601e",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "ingestion_request_id = None\n",
+ "\n",
+ "if not destination_id:\n",
+ " print(\n",
+ " \"Error: destination_id is not set. Cannot ingest data. Please ensure the User List was created successfully in the previous step.\"\n",
+ " )\n",
+ "else:\n",
+ " print(f\"Ingesting sample data to User List {destination_id}...\")\n",
+ "\n",
+ " destination_obj = datamanager_v1.Destination(\n",
+ " login_account=datamanager_v1.ProductAccount(\n",
+ " account_type=DATA_PARTNER, account_id=login_account_id\n",
+ " ),\n",
+ " operating_account=datamanager_v1.ProductAccount(\n",
+ " account_type=GOOGLE_ADS, account_id=operating_account_id\n",
+ " ),\n",
+ " linked_account=datamanager_v1.ProductAccount(\n",
+ " account_type=GOOGLE_ADS, account_id=linked_account_id\n",
+ " ),\n",
+ " product_destination_id=str(destination_id),\n",
+ " )\n",
+ "\n",
+ " ingest_payload = datamanager_v1.IngestAudienceMembersRequest(\n",
+ " consent=datamanager_v1.Consent(\n",
+ " ad_user_data=CONSENT_GRANTED, ad_personalization=CONSENT_GRANTED\n",
+ " ),\n",
+ " encoding=datamanager_v1.Encoding.HEX,\n",
+ " terms_of_service=datamanager_v1.TermsOfService(\n",
+ " customer_match_terms_of_service_status=\"ACCEPTED\"\n",
+ " ),\n",
+ " validate_only=False,\n",
+ " audience_members=[\n",
+ " datamanager_v1.AudienceMember(\n",
+ " user_data=datamanager_v1.UserData(\n",
+ " user_identifiers=[\n",
+ " datamanager_v1.UserIdentifier(\n",
+ " email_address=\"223EBDA6F6889B1494551BA902D9D381DAF2F642BAE055888E96343D53E9F9C4\"\n",
+ " ),\n",
+ " datamanager_v1.UserIdentifier(\n",
+ " email_address=\"F1FCDE379F31F4D446B76EE8F34860ECA2288ADC6B6D6C0FDC56D9EEE75A2FA5\"\n",
+ " ),\n",
+ " ]\n",
+ " )\n",
+ " )\n",
+ " ],\n",
+ " destinations=[destination_obj],\n",
+ " )\n",
+ "\n",
+ " try:\n",
+ " ingest_res = partner_sdk.ingestion_service.ingest_audience_members(\n",
+ " request=ingest_payload\n",
+ " )\n",
+ "\n",
+ " ingestion_request_id = ingest_res.request_id\n",
+ " print(f\"Ingestion Submitted!\")\n",
+ " print(f\"Request ID: {ingestion_request_id}\")\n",
+ "\n",
+ " except Exception as e:\n",
+ " print(f\"Failed to ingest data: {e}\")\n",
+ "\n",
+ " print(\n",
+ " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/audienceMembers/ingest?apix=true\"\n",
+ " )\n",
+ " print(\"\\n--- Request JSON Payload ---\")\n",
+ " print(MessageToJson(ingest_payload._pb))\n",
+ " print(\"----------------------------\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c4eab526",
+ "metadata": {
+ "id": "DcXOh5xJfYdh"
+ },
+ "source": [
+ "### Step 4. Check Status"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "b69395e4",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "if not ingestion_request_id:\n",
+ " print(\"Status check skipped because ingestion request ID is not set.\")\n",
+ "else:\n",
+ " print(f\"Checking status for Request ID: {ingestion_request_id}...\")\n",
+ "\n",
+ " # Note: Headers aren't needed for this request.\n",
+ " status_req = datamanager_v1.RetrieveRequestStatusRequest(\n",
+ " request_id=ingestion_request_id\n",
+ " )\n",
+ "\n",
+ " try:\n",
+ " status_res = partner_sdk.ingestion_service.retrieve_request_status(\n",
+ " request=status_req\n",
+ " )\n",
+ "\n",
+ " print(f\"Successfully retrieved status!\")\n",
+ "\n",
+ " for dest_status in status_res.request_status_per_destination:\n",
+ " dest_id = dest_status.destination.product_destination_id\n",
+ " current_status = dest_status.request_status.name\n",
+ "\n",
+ " print(f\"Status for destination {dest_id}: {current_status}\")\n",
+ "\n",
+ " except Exception as e:\n",
+ " print(f\"Failed to retrieve status: {e}\")\n",
+ "\n",
+ " print(\n",
+ " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/requestStatus/retrieve?apix=true\"\n",
+ " )\n",
+ " print(\"\\n--- Request JSON Payload ---\")\n",
+ " print(MessageToJson(status_req._pb))\n",
+ " print(\"----------------------------\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "colab": {
+ "private_outputs": true,
+ "provenance": []
+ },
+ "kernelspec": {
+ "display_name": ".venv",
+ "language": "python",
+ "name": "python3"
+ },
+ "language_info": {
+ "name": "python",
+ "version": "3.13.12"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/notebooks/audience_e2e_data_partner_simplified.ipynb b/notebooks/audience_e2e_data_partner_simplified.ipynb
new file mode 100644
index 0000000..ac6485c
--- /dev/null
+++ b/notebooks/audience_e2e_data_partner_simplified.ipynb
@@ -0,0 +1,582 @@
+{
+ "cells": [
+ {
+ "cell_type": "markdown",
+ "id": "7c0fc11f",
+ "metadata": {
+ "id": "h2sCjFoFUEYZ"
+ },
+ "source": [
+ "Project: /data-manager/api/_project.yaml\n",
+ "Book: /data-manager/api/_book.yaml\n",
+ "\n",
+ ""
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "549fb865",
+ "metadata": {
+ "cellView": "form",
+ "id": "ZL8ttX6NRUE0"
+ },
+ "outputs": [],
+ "source": [
+ "# @markdown #### Copyright 2026 Google LLC\n",
+ "# @markdown ##### Licensed under the Apache License, Version 2.0 (the \"License\");\n",
+ "# you may not use this file except in compliance with the License.\n",
+ "# You may obtain a copy of the License at\n",
+ "#\n",
+ "# https://www.apache.org/licenses/LICENSE-2.0\n",
+ "#\n",
+ "# Unless required by applicable law or agreed to in writing, software\n",
+ "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
+ "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
+ "# See the License for the specific language governing permissions and\n",
+ "# limitations under the License."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "b1b6c27f",
+ "metadata": {},
+ "source": [
+ "# Data Partner: Simplified Testing Workflow"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "e54c6728",
+ "metadata": {},
+ "source": [
+ "## Objective\n",
+ "This notebook provides a simplified testing workflow for the Data Manager API as a Data Partner.\n",
+ "It uses a Service Account (or OAuth) to authenticate, assuming the authenticated user has ADMIN access to the Google Ads account.\n",
+ "\n",
+ "## Prerequisites\n",
+ "\n",
+ "The email address of a [service account](//cloud.google.com/docs/authentication#service-accounts).\n",
+ "\n",
+ "[Follow these instructions](https://developers.google.com/data-manager/api/devguides/quickstart/set-up-access#service-account) if you don't have a service account.\n",
+ "\n",
+ "## Instructions\n",
+ "1. Make a copy of this Colab.\n",
+ "2. Fill in the required account IDs and Service Account details in the Setup cell.\n",
+ "3. Run your copy of the Colab."
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "d8eb38f6",
+ "metadata": {
+ "id": "-wtv73t1egvl"
+ },
+ "source": [
+ "### Step 0. Install and Import Packages"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1895d4d6",
+ "metadata": {
+ "cellView": "form",
+ "collapsed": true,
+ "id": "j9kMcKunX3dd"
+ },
+ "outputs": [],
+ "source": [
+ "!pip install --upgrade google-ads-datamanager google-auth-oauthlib"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "95962eb6",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "import datetime\n",
+ "from google.colab import userdata\n",
+ "from google.ads import datamanager_v1\n",
+ "import google.auth\n",
+ "from google.protobuf.json_format import MessageToJson"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "82b72a62",
+ "metadata": {
+ "id": "81MXNTkJezi9"
+ },
+ "source": [
+ "### Step 1. Setup and Configuration"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8017d907",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "# @markdown ### Account IDs\n",
+ "# @markdown Provide the advertiser and data partner account IDs below.\n",
+ "# @markdown - **[required]** `login_account_id`: Your Data Partner account.\n",
+ "# @markdown - **[required]** `operating_account_id`: The Google Ads advertiser account that you want to link to your data partner account. This account will own the audience you create as part of this workflow.\n",
+ "login_account_id = \"\" # @param {type:\"string\"}\n",
+ "operating_account_id = \"\" # @param {type:\"string\"}\n",
+ "\n",
+ "# @markdown ### Service Account Details\n",
+ "service_account_email = \"\" # @param {type:\"string\"}\n",
+ "\n",
+ "# Clean up account IDs by removing hyphens.\n",
+ "login_account_id = login_account_id.replace(\"-\", \"\")\n",
+ "operating_account_id = operating_account_id.replace(\"-\", \"\")\n",
+ "\n",
+ "GOOGLE_ADS = \"GOOGLE_ADS\"\n",
+ "DATA_PARTNER = \"DATA_PARTNER\"\n",
+ "CONSENT_GRANTED = \"CONSENT_GRANTED\""
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "a2a54e52",
+ "metadata": {
+ "id": "Kv3wnXRDfBgY"
+ },
+ "source": [
+ "### Step 2. Initialize Client"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "6a3e3115",
+ "metadata": {},
+ "outputs": [],
+ "source": [
+ "class DataManagerSDK:\n",
+ " def __init__(self, creds):\n",
+ " self.link_service = datamanager_v1.PartnerLinkServiceClient(credentials=creds)\n",
+ " self.user_list_service = datamanager_v1.UserListServiceClient(credentials=creds)\n",
+ " self.ingestion_service = datamanager_v1.IngestionServiceClient(credentials=creds)\n",
+ "\n",
+ "def initialize_client():\n",
+ " data_manager_scope = \"https://www.googleapis.com/auth/datamanager\";\n",
+ " print(\"Authenticating with Service Account...\")\n",
+ " auth_command = (\n",
+ " f\"gcloud auth application-default login \"\n",
+ " f\"--impersonate-service-account={service_account_email} \"\n",
+ " f\"--scopes={data_manager_scope},https://www.googleapis.com/auth/cloud-platform \"\n",
+ " f\"--no-browser\"\n",
+ " )\n",
+ " get_ipython().system(auth_command)\n",
+ " creds, project = google.auth.default(scopes=[data_manager_scope])\n",
+ " return DataManagerSDK(creds)\n",
+ "\n",
+ "sdk = initialize_client()\n",
+ "print(\"SDK Services Initialized Successfully\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "c88699c5",
+ "metadata": {
+ "id": "--2LPM7_fKZM"
+ },
+ "source": [
+ "### Step 3. Create Partner Link"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "1086aeed",
+ "metadata": {
+ "id": "V8Fk5jI4aCjO"
+ },
+ "outputs": [],
+ "source": [
+ "print(f\"3. Creating link for Ads {operating_account_id}..\\n\")\n",
+ "\n",
+ "parent_ads = f\"accountTypes/GOOGLE_ADS/accounts/{operating_account_id}\"\n",
+ "\n",
+ "partner_link_data = datamanager_v1.PartnerLink(\n",
+ " owning_account=datamanager_v1.ProductAccount(\n",
+ " account_id=operating_account_id, account_type=GOOGLE_ADS\n",
+ " ),\n",
+ " partner_account=datamanager_v1.ProductAccount(\n",
+ " account_id=login_account_id, account_type=DATA_PARTNER\n",
+ " ),\n",
+ ")\n",
+ "\n",
+ "try:\n",
+ " link_res = sdk.link_service.create_partner_link(\n",
+ " parent=parent_ads, partner_link=partner_link_data\n",
+ " )\n",
+ " print(f\"Link Created: {link_res.partner_link_id}\")\n",
+ " product_link_id = link_res.partner_link_id\n",
+ "except Exception as e:\n",
+ " print(f\"Link creation failed or exists: {e}\")\n",
+ " print(\"\\nSearching for existing link...\")\n",
+ " search_res = sdk.link_service.search_partner_links(\n",
+ " parent=f\"accountTypes/DATA_PARTNER/accounts/{login_account_id}\"\n",
+ " )\n",
+ " # The search result is an iterator\n",
+ " for link in search_res:\n",
+ " if link.owning_account.account_id == operating_account_id:\n",
+ " product_link_id = link.partner_link_id\n",
+ " print(f\"Found existing Link ID: {product_link_id}\")\n",
+ " break\n",
+ "\n",
+ "print(\n",
+ " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/accountTypes.accounts.partnerLinks/create?apix=true\"\n",
+ ")\n",
+ "print(f\"\\nparent: {parent_ads}\")\n",
+ "print(\"\\n--- Request JSON Payload ---\")\n",
+ "print(MessageToJson(partner_link_data._pb))\n",
+ "print(\"----------------------------\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "50115d94",
+ "metadata": {
+ "id": "89jsXS8jfQSd"
+ },
+ "source": [
+ "### Step 4. Create User List"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "be6f5aaf",
+ "metadata": {
+ "id": "UBpVcR_yzHoQ"
+ },
+ "outputs": [],
+ "source": [
+ "print(f\"4. Creating User List in {operating_account_id}...\\n\")\n",
+ "\n",
+ "parent_userlist = f\"accountTypes/GOOGLE_ADS/accounts/{operating_account_id}\"\n",
+ "\n",
+ "user_list_data = datamanager_v1.UserList(\n",
+ " display_name=f\"Python SDK Audience - {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\",\n",
+ " ingested_user_list_info=datamanager_v1.IngestedUserListInfo(\n",
+ " upload_key_types=[\"CONTACT_ID\"]\n",
+ " ),\n",
+ ")\n",
+ "\n",
+ "# Creates a dictionary to set headers.\n",
+ "login_account = f\"accountTypes/DATA_PARTNER/accounts/{login_account_id}\"\n",
+ "linked_account = f\"accountTypes/GOOGLE_ADS/accounts/{operating_account_id}\"\n",
+ "\n",
+ "headers = [\n",
+ " (\"login-account\", login_account),\n",
+ " (\"linked-account\", linked_account),\n",
+ "]\n",
+ "\n",
+ "destination_id = None\n",
+ "try:\n",
+ " ulist_res = sdk.user_list_service.create_user_list(\n",
+ " parent=parent_userlist, user_list=user_list_data, metadata=headers\n",
+ " )\n",
+ "\n",
+ " destination_id = ulist_res.id\n",
+ "\n",
+ " print(f\"User List Created!\")\n",
+ " print(f\"Destination ID: {destination_id}\")\n",
+ " print(f\"Resource Name: {ulist_res.name}\")\n",
+ " print(f\"Display Name: {ulist_res.display_name}\")\n",
+ "\n",
+ "except Exception as e:\n",
+ " print(f\"Failed to create User List: {e}\")\n",
+ "\n",
+ "print(\n",
+ " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/accountTypes.accounts.userLists/create?apix=true\"\n",
+ ")\n",
+ "print(f\"\\nparent: {parent_userlist}\")\n",
+ "\n",
+ "print(f\"login-account: {login_account}\")\n",
+ "print(f\"linked-account: {linked_account}\")\n",
+ "\n",
+ "print(\"\\n--- Request JSON Payload ---\")\n",
+ "print(MessageToJson(user_list_data._pb))\n",
+ "print(\"----------------------------\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "94d1c221",
+ "metadata": {
+ "id": "CrmA_yg0fUad"
+ },
+ "source": [
+ "### Step 5. Ingest Data"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a9efe18a",
+ "metadata": {
+ "id": "iTYvfXYnM-v-"
+ },
+ "outputs": [],
+ "source": [
+ "ingestion_request_id = None\n",
+ "\n",
+ "if not destination_id:\n",
+ " print(\n",
+ " \"Error: destination_id is not set. Cannot ingest data. Please ensure the User List was created successfully in the previous step.\"\n",
+ " )\n",
+ "else:\n",
+ " print(f\"5. Ingesting sample data to User List {destination_id}...\")\n",
+ "\n",
+ " # Build the Destination object explicitly based on the persona\n",
+ " destination_obj = datamanager_v1.Destination(\n",
+ " login_account=datamanager_v1.ProductAccount(\n",
+ " account_type=DATA_PARTNER, account_id=login_account_id\n",
+ " ),\n",
+ " operating_account=datamanager_v1.ProductAccount(\n",
+ " account_type=GOOGLE_ADS, account_id=operating_account_id\n",
+ " ),\n",
+ " product_destination_id=str(destination_id),\n",
+ " )\n",
+ "\n",
+ " # Add the destination_obj directly to the payload\n",
+ " ingest_payload = datamanager_v1.IngestAudienceMembersRequest(\n",
+ " consent=datamanager_v1.Consent(\n",
+ " ad_user_data=CONSENT_GRANTED, ad_personalization=CONSENT_GRANTED\n",
+ " ),\n",
+ " encoding=datamanager_v1.Encoding.HEX,\n",
+ " terms_of_service=datamanager_v1.TermsOfService(\n",
+ " customer_match_terms_of_service_status=\"ACCEPTED\"\n",
+ " ),\n",
+ " validate_only=False,\n",
+ " audience_members=[\n",
+ " datamanager_v1.AudienceMember(\n",
+ " user_data=datamanager_v1.UserData(\n",
+ " user_identifiers=[\n",
+ " # NOTE: email_address values must be hex-encoded SHA-256\n",
+ " # hashes of the normalized email addresses (lowercase, dots\n",
+ " # removed from gmail, etc.).\n",
+ " # Do NOT use plain text email addresses here.\n",
+ " # See Data Manager API documentation for formatting rules:\n",
+ " # https://developers.google.com/data-manager/api/devguides/concepts/formatting\n",
+ " datamanager_v1.UserIdentifier(\n",
+ " email_address=\"223EBDA6F6889B1494551BA902D9D381DAF2F642BAE055888E96343D53E9F9C4\"\n",
+ " ),\n",
+ " datamanager_v1.UserIdentifier(\n",
+ " email_address=\"F1FCDE379F31F4D446B76EE8F34860ECA2288ADC6B6D6C0FDC56D9EEE75A2FA5\"\n",
+ " ),\n",
+ " ]\n",
+ " )\n",
+ " )\n",
+ " ],\n",
+ " destinations=[destination_obj],\n",
+ " )\n",
+ "\n",
+ " try:\n",
+ " ingest_res = sdk.ingestion_service.ingest_audience_members(\n",
+ " request=ingest_payload\n",
+ " )\n",
+ "\n",
+ " ingestion_request_id = ingest_res.request_id\n",
+ " print(f\"Ingestion Submitted!\")\n",
+ " print(f\"Request ID: {ingestion_request_id}\")\n",
+ "\n",
+ " except Exception as e:\n",
+ " print(f\"Failed to ingest data: {e}\")\n",
+ "\n",
+ " print(\n",
+ " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/audienceMembers/ingest?apix=true\"\n",
+ " )\n",
+ " print(\"\\n--- Request JSON Payload ---\")\n",
+ " print(MessageToJson(ingest_payload._pb))\n",
+ " print(\"----------------------------\\n\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "5fedc5fa",
+ "metadata": {
+ "id": "DcXOh5xJfYdh"
+ },
+ "source": [
+ "### Step 6. Check Status"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "8e056b4d",
+ "metadata": {
+ "id": "DiEenmMYiIN3"
+ },
+ "outputs": [],
+ "source": [
+ "if not ingestion_request_id:\n",
+ " print(\"Status check skipped because ingestion request ID is not set.\")\n",
+ "else:\n",
+ " print(f\"6. Checking status for Request ID: {ingestion_request_id}...\")\n",
+ "\n",
+ " # Note: Headers aren't needed for this request.\n",
+ " status_req = datamanager_v1.RetrieveRequestStatusRequest(\n",
+ " request_id=ingestion_request_id\n",
+ " )\n",
+ "\n",
+ " try:\n",
+ " status_res = sdk.ingestion_service.retrieve_request_status(\n",
+ " request=status_req\n",
+ " )\n",
+ "\n",
+ " print(f\"Successfully retrieved status!\")\n",
+ "\n",
+ " for dest_status in status_res.request_status_per_destination:\n",
+ " dest_id = dest_status.destination.product_destination_id\n",
+ " current_status = dest_status.request_status.name\n",
+ "\n",
+ " print(f\"Status for destination {dest_id}: {current_status}\")\n",
+ "\n",
+ " except Exception as e:\n",
+ " print(f\"Failed to retrieve status: {e}\")\n",
+ "\n",
+ " print(\n",
+ " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/requestStatus/retrieve?apix=true\"\n",
+ " )\n",
+ " print(\"\\n--- Request JSON Payload ---\")\n",
+ " print(MessageToJson(status_req._pb))\n",
+ " print(\"----------------------------\")"
+ ]
+ },
+ {
+ "cell_type": "markdown",
+ "id": "9d5671dc",
+ "metadata": {
+ "id": "gYjD5Fzgfbzu"
+ },
+ "source": [
+ "### Step 7. [Optional] Remove Product Link"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "id": "a61d62ee",
+ "metadata": {
+ "id": "7NY8t6Y3jSL-"
+ },
+ "outputs": [],
+ "source": [
+ "if product_link_id:\n",
+ " print(f\"\\nAttempting to remove product link {product_link_id}...\")\n",
+ "\n",
+ " link_resource_name = f\"accountTypes/GOOGLE_ADS/accounts/{operating_account_id}/partnerLinks/{product_link_id}\"\n",
+ "\n",
+ " delete_req = datamanager_v1.DeletePartnerLinkRequest(\n",
+ " name=link_resource_name\n",
+ " )\n",
+ "\n",
+ " try:\n",
+ " sdk.link_service.delete_partner_link(request=delete_req)\n",
+ " print(\"Successfully removed product link.\")\n",
+ "\n",
+ " except Exception as e:\n",
+ " print(f\"Failed to remove product link: {e}\")\n",
+ "else:\n",
+ " print(\"\\nNo product_link_id found to delete. Did you run Step 1?\")\n",
+ "\n",
+ "print(\n",
+ " \"\\nAPIx link: https://developers.google.com/data-manager/api/reference/rest/v1/accountTypes.accounts.partnerLinks/delete?apix=true\"\n",
+ ")\n",
+ "print(\"\\n--- Request JSON Payload ---\")\n",
+ "print(MessageToJson(delete_req._pb))\n",
+ "print(\"----------------------------\")"
+ ]
+ }
+ ],
+ "metadata": {
+ "colab": {
+ "private_outputs": true,
+ "provenance": []
+ },
+ "kernelspec": {
+ "display_name": "Python 3",
+ "name": "python3"
+ },
+ "language_info": {
+ "name": "python"
+ }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 0
+}
diff --git a/pyproject.toml b/pyproject.toml
index eaf9e3e..c78e298 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -46,7 +46,7 @@ dependencies = [
[project.optional-dependencies]
dev = [
- "black",
+ "black[jupyter]",
"nox >= 2020.12.31, < 2022.6",
# Include the dependencies for samples for convenience, since sample updates
# are often part of the dev process.